diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 8446235fb1881..a17c7626148ab 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -204,6 +204,7 @@ pub(crate) struct IndexItemFunctionType { inputs: Vec, output: Vec, where_clause: Vec>, + param_names: Vec, } impl IndexItemFunctionType { diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index d1939adc1a501..ee2fc1f44b345 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -646,9 +646,25 @@ pub(crate) fn build_index<'tcx>( full_paths.push((*index, path)); } + let param_names: Vec<(usize, String)> = { + let mut prev = Vec::new(); + let mut result = Vec::new(); + for (index, item) in self.items.iter().enumerate() { + if let Some(ty) = &item.search_type + && let my = + ty.param_names.iter().map(|sym| sym.as_str()).collect::>() + && my != prev + { + result.push((index, my.join(","))); + prev = my; + } + } + result + }; + let has_aliases = !self.aliases.is_empty(); let mut crate_data = - serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?; + serializer.serialize_struct("CrateData", if has_aliases { 13 } else { 12 })?; crate_data.serialize_field("t", &types)?; crate_data.serialize_field("n", &names)?; crate_data.serialize_field("q", &full_paths)?; @@ -660,6 +676,7 @@ pub(crate) fn build_index<'tcx>( crate_data.serialize_field("b", &self.associated_item_disambiguators)?; crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?; crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?; + crate_data.serialize_field("P", ¶m_names)?; if has_aliases { crate_data.serialize_field("a", &self.aliases)?; } @@ -758,7 +775,7 @@ pub(crate) fn get_function_type_for_search<'tcx>( None } }); - let (mut inputs, mut output, where_clause) = match item.kind { + let (mut inputs, mut output, param_names, where_clause) = match item.kind { clean::ForeignFunctionItem(ref f, _) | clean::FunctionItem(ref f) | clean::MethodItem(ref f, _) @@ -771,7 +788,7 @@ pub(crate) fn get_function_type_for_search<'tcx>( inputs.retain(|a| a.id.is_some() || a.generics.is_some()); output.retain(|a| a.id.is_some() || a.generics.is_some()); - Some(IndexItemFunctionType { inputs, output, where_clause }) + Some(IndexItemFunctionType { inputs, output, where_clause, param_names }) } fn get_index_type( @@ -1285,7 +1302,7 @@ fn get_fn_inputs_and_outputs<'tcx>( tcx: TyCtxt<'tcx>, impl_or_trait_generics: Option<&(clean::Type, clean::Generics)>, cache: &Cache, -) -> (Vec, Vec, Vec>) { +) -> (Vec, Vec, Vec, Vec>) { let decl = &func.decl; let mut rgen: FxIndexMap)> = Default::default(); @@ -1331,7 +1348,21 @@ fn get_fn_inputs_and_outputs<'tcx>( let mut ret_types = Vec::new(); simplify_fn_type(self_, generics, &decl.output, tcx, 0, &mut ret_types, &mut rgen, true, cache); - let mut simplified_params = rgen.into_values().collect::>(); - simplified_params.sort_by_key(|(idx, _)| -idx); - (arg_types, ret_types, simplified_params.into_iter().map(|(_idx, traits)| traits).collect()) + let mut simplified_params = rgen.into_iter().collect::>(); + simplified_params.sort_by_key(|(_, (idx, _))| -idx); + ( + arg_types, + ret_types, + simplified_params + .iter() + .map(|(name, (_idx, _traits))| match name { + SimplifiedParam::Symbol(name) => *name, + SimplifiedParam::Anonymous(_) => kw::Empty, + SimplifiedParam::AssociatedType(def_id, name) => { + Symbol::intern(&format!("{}::{}", tcx.item_name(*def_id), name)) + } + }) + .collect(), + simplified_params.into_iter().map(|(_name, (_idx, traits))| traits).collect(), + ) } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 1042d254749e3..66a8a19892886 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -264,6 +264,7 @@ a.anchor, .mobile-topbar h2 a, h1 a, .search-results a, +.search-results li, .stab, .result-name i { color: var(--main-color); @@ -379,7 +380,7 @@ details:not(.toggle) summary { margin-bottom: .6em; } -code, pre, .code-header { +code, pre, .code-header, .type-signature { font-family: "Source Code Pro", monospace; } .docblock code, .docblock-short code { @@ -1205,22 +1206,28 @@ so that we can apply CSS-filters to change the arrow color in themes */ .search-results.active { display: block; + margin: 0; + padding: 0; } .search-results > a { - display: flex; + display: grid; + grid-template-areas: + "search-result-name search-result-desc" + "search-result-type-signature search-result-type-signature"; + grid-template-columns: .6fr .4fr; /* A little margin ensures the browser's outlining of focused links has room to display. */ margin-left: 2px; margin-right: 2px; border-bottom: 1px solid var(--search-result-border-color); - gap: 1em; + column-gap: 1em; } .search-results > a > div.desc { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; - flex: 2; + grid-area: search-result-desc; } .search-results a:hover, @@ -1232,7 +1239,7 @@ so that we can apply CSS-filters to change the arrow color in themes */ display: flex; align-items: center; justify-content: start; - flex: 3; + grid-area: search-result-name; } .search-results .result-name .alias { color: var(--search-results-alias-color); @@ -1253,6 +1260,10 @@ so that we can apply CSS-filters to change the arrow color in themes */ .search-results .result-name .path > * { display: inline; } +.search-results .type-signature { + grid-area: search-result-type-signature; + white-space: pre-wrap; +} .popover { position: absolute; diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js index 3d3e0a8f8381c..c4faca1c0c3bc 100644 --- a/src/librustdoc/html/static/js/externs.js +++ b/src/librustdoc/html/static/js/externs.js @@ -9,12 +9,12 @@ function initSearch(searchIndex){} /** * @typedef {{ * name: string, - * id: integer|null, + * id: number|null, * fullPath: Array, * pathWithoutLast: Array, * pathLast: string, * generics: Array, - * bindings: Map>, + * bindings: Map>, * }} */ let QueryElement; @@ -92,6 +92,9 @@ let Results; * parent: (Object|undefined), * path: string, * ty: number, + * type: FunctionSearchType?, + * displayType: Promise>>|null, + * displayTypeMappedNames: Promise]>>|null, * }} */ let ResultObject; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index eed64d024c046..0458b81d35257 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -15,7 +15,16 @@ if (!Array.prototype.toSpliced) { }; } -(function() { +function onEachBtwn(arr, func, funcBtwn) { + let skipped = true; + for (const value of arr) { + if (!skipped) { + funcBtwn(value); + } + skipped = func(value); + } +} + // ==================== Core search logic begin ==================== // This mapping table should match the discriminants of // `rustdoc::formats::item_type::ItemType` type in Rust. @@ -50,8 +59,10 @@ const itemTypes = [ ]; // used for special search precedence +const TY_PRIMITIVE = itemTypes.indexOf("primitive"); const TY_GENERIC = itemTypes.indexOf("generic"); const TY_IMPORT = itemTypes.indexOf("import"); +const TY_TRAIT = itemTypes.indexOf("trait"); const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../"; // Hard limit on how deep to recurse into generics when doing type-driven search. @@ -276,7 +287,7 @@ function getFilteredNextElem(query, parserState, elems, isInGenerics) { // The type filter doesn't count as an element since it's a modifier. const typeFilterElem = elems.pop(); checkExtraTypeFilterCharacters(start, parserState); - parserState.typeFilter = typeFilterElem.name; + parserState.typeFilter = typeFilterElem.normalizedPathLast; parserState.pos += 1; parserState.totalElems -= 1; query.literalSearch = false; @@ -686,7 +697,7 @@ function createQueryElement(query, parserState, name, generics, isInGenerics) { } else if (quadcolon !== null) { throw ["Unexpected ", quadcolon[0]]; } - const pathSegments = path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/); + const pathSegments = path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/).map(x => x.toLowerCase()); // In case we only have something like `

`, there is no name. if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === "")) { @@ -726,7 +737,10 @@ function createQueryElement(query, parserState, name, generics, isInGenerics) { if (gen.name !== null) { gen.bindingName.generics.unshift(gen); } - bindings.set(gen.bindingName.name, gen.bindingName.generics); + bindings.set( + gen.bindingName.name.toLowerCase().replace(/_/g, ""), + gen.bindingName.generics, + ); return false; } return true; @@ -1114,6 +1128,13 @@ class DocSearch { * @type {Map} */ this.typeNameIdMap = new Map(); + /** + * Map from type ID to associated type name. Used for display, + * not for search. + * + * @type {Map} + */ + this.assocTypeIdNameMap = new Map(); this.ALIASES = new Map(); this.rootPath = rootPath; this.searchState = searchState; @@ -1158,6 +1179,14 @@ class DocSearch { * Special type name IDs for searching higher order functions (`->` syntax). */ this.typeNameIdOfHof = this.buildTypeMapIndex("->"); + /** + * Special type name IDs the output assoc type. + */ + this.typeNameIdOfOutput = this.buildTypeMapIndex("output", true); + /** + * Special type name IDs for searching by reference. + */ + this.typeNameIdOfReference = this.buildTypeMapIndex("reference"); /** * Empty, immutable map used in item search types with no bindings. @@ -1234,9 +1263,9 @@ class DocSearch { * * @return {Array} */ - buildItemSearchTypeAll(types, lowercasePaths) { + buildItemSearchTypeAll(types, paths, lowercasePaths) { return types.length > 0 ? - types.map(type => this.buildItemSearchType(type, lowercasePaths)) : + types.map(type => this.buildItemSearchType(type, paths, lowercasePaths)) : this.EMPTY_GENERICS_ARRAY; } @@ -1245,7 +1274,7 @@ class DocSearch { * * @param {RawFunctionType} type */ - buildItemSearchType(type, lowercasePaths, isAssocType) { + buildItemSearchType(type, paths, lowercasePaths, isAssocType) { const PATH_INDEX_DATA = 0; const GENERICS_DATA = 1; const BINDINGS_DATA = 2; @@ -1258,6 +1287,7 @@ class DocSearch { pathIndex = type[PATH_INDEX_DATA]; generics = this.buildItemSearchTypeAll( type[GENERICS_DATA], + paths, lowercasePaths, ); if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) { @@ -1274,8 +1304,8 @@ class DocSearch { // // As a result, the key should never have generics on it. return [ - this.buildItemSearchType(assocType, lowercasePaths, true).id, - this.buildItemSearchTypeAll(constraints, lowercasePaths), + this.buildItemSearchType(assocType, paths, lowercasePaths, true).id, + this.buildItemSearchTypeAll(constraints, paths, lowercasePaths), ]; })); } else { @@ -1291,6 +1321,7 @@ class DocSearch { // the actual names of generic parameters aren't stored, since they aren't API result = { id: pathIndex, + name: "", ty: TY_GENERIC, path: null, exactPath: null, @@ -1301,6 +1332,7 @@ class DocSearch { // `0` is used as a sentinel because it's fewer bytes than `null` result = { id: null, + name: "", ty: null, path: null, exactPath: null, @@ -1309,8 +1341,13 @@ class DocSearch { }; } else { const item = lowercasePaths[pathIndex - 1]; + const id = this.buildTypeMapIndex(item.name, isAssocType); + if (isAssocType) { + this.assocTypeIdNameMap.set(id, paths[pathIndex - 1].name); + } result = { - id: this.buildTypeMapIndex(item.name, isAssocType), + id, + name: paths[pathIndex - 1].name, ty: item.ty, path: item.path, exactPath: item.exactPath, @@ -1352,7 +1389,7 @@ class DocSearch { } if (cr.ty === result.ty && cr.path === result.path && cr.bindings === result.bindings && cr.generics === result.generics - && cr.ty === result.ty + && cr.ty === result.ty && cr.name === result.name ) { return cr; } @@ -1463,11 +1500,12 @@ class DocSearch { * The raw function search type format is generated using serde in * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string * + * @param {Array<{name: string, ty: number}>} paths * @param {Array<{name: string, ty: number}>} lowercasePaths * * @return {null|FunctionSearchType} */ - const buildFunctionSearchTypeCallback = lowercasePaths => { + const buildFunctionSearchTypeCallback = (paths, lowercasePaths) => { return functionSearchType => { if (functionSearchType === 0) { return null; @@ -1477,11 +1515,16 @@ class DocSearch { let inputs, output; if (typeof functionSearchType[INPUTS_DATA] === "number") { inputs = [ - this.buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths), + this.buildItemSearchType( + functionSearchType[INPUTS_DATA], + paths, + lowercasePaths, + ), ]; } else { inputs = this.buildItemSearchTypeAll( functionSearchType[INPUTS_DATA], + paths, lowercasePaths, ); } @@ -1490,12 +1533,14 @@ class DocSearch { output = [ this.buildItemSearchType( functionSearchType[OUTPUT_DATA], + paths, lowercasePaths, ), ]; } else { output = this.buildItemSearchTypeAll( functionSearchType[OUTPUT_DATA], + paths, lowercasePaths, ); } @@ -1506,8 +1551,12 @@ class DocSearch { const l = functionSearchType.length; for (let i = 2; i < l; ++i) { where_clause.push(typeof functionSearchType[i] === "number" - ? [this.buildItemSearchType(functionSearchType[i], lowercasePaths)] - : this.buildItemSearchTypeAll(functionSearchType[i], lowercasePaths)); + ? [this.buildItemSearchType(functionSearchType[i], paths, lowercasePaths)] + : this.buildItemSearchTypeAll( + functionSearchType[i], + paths, + lowercasePaths, + )); } return { inputs, output, where_clause, @@ -1551,6 +1600,13 @@ class DocSearch { this.searchIndexEmptyDesc.set(crate, new RoaringBitmap(crateCorpus.e)); let descIndex = 0; + /** + * List of generic function type parameter names. + * Used for display, not for searching. + * @type {[string]} + */ + let lastParamNames = []; + // This object should have exactly the same set of fields as the "row" // object defined below. Your JavaScript runtime will thank you. // https://mathiasbynens.be/notes/shapes-ics @@ -1565,6 +1621,7 @@ class DocSearch { desc: crateCorpus.doc, parent: undefined, type: null, + paramNames: lastParamNames, id, word: crate, normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""), @@ -1601,6 +1658,10 @@ class DocSearch { // an array of [(String) alias name // [Number] index to items] const aliases = crateCorpus.a; + // an array of [(Number) item index, + // (String) comma-separated list of function generic param names] + // an item whose index is not present will fall back to the previous present path + const itemParamNames = new Map(crateCorpus.P); // an array of [{name: String, ty: Number}] const lowercasePaths = []; @@ -1608,7 +1669,7 @@ class DocSearch { // a string representing the list of function types const itemFunctionDecoder = new VlqHexDecoder( crateCorpus.f, - buildFunctionSearchTypeCallback(lowercasePaths), + buildFunctionSearchTypeCallback(paths, lowercasePaths), ); // convert `rawPaths` entries into object form @@ -1659,6 +1720,9 @@ class DocSearch { const name = itemNames[i] === "" ? lastName : itemNames[i]; const word = itemNames[i] === "" ? lastWord : itemNames[i].toLowerCase(); const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; + const paramNames = itemParamNames.has(i) ? + itemParamNames.get(i).split(",") : + lastParamNames; const type = itemFunctionDecoder.next(); if (type !== null) { if (type) { @@ -1691,6 +1755,7 @@ class DocSearch { itemPaths.get(itemReexports.get(i)) : path, parent: itemParentIdx > 0 ? paths[itemParentIdx - 1] : undefined, type, + paramNames, id, word, normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""), @@ -1701,6 +1766,7 @@ class DocSearch { id += 1; searchIndex.push(row); lastPath = row.path; + lastParamNames = row.paramNames; if (!this.searchIndexEmptyDesc.get(crate).contains(bitIndex)) { descIndex += 1; } @@ -1786,8 +1852,7 @@ class DocSearch { */ function newParsedQuery(userQuery) { return { - original: userQuery, - userQuery: userQuery.toLowerCase(), + userQuery, elems: [], returned: [], // Total number of "top" elements (does not include generics). @@ -1909,7 +1974,7 @@ class DocSearch { genericsElems: 0, typeFilter: null, isInBinding: null, - userQuery: userQuery.toLowerCase(), + userQuery, }; let query = newParsedQuery(userQuery); @@ -2046,18 +2111,21 @@ class DocSearch { * marked for removal. * * @param {[ResultObject]} results + * @param {"sig"|"elems"|"returned"|null} typeInfo + * @param {ParsedQuery} query * @returns {[ResultObject]} */ - const transformResults = results => { + const transformResults = (results, typeInfo) => { const duplicates = new Set(); const out = []; for (const result of results) { if (result.id !== -1) { - const obj = this.searchIndex[result.id]; - obj.dist = result.dist; - const res = buildHrefAndPath(obj); - obj.displayPath = pathSplitter(res[0]); + const res = buildHrefAndPath(this.searchIndex[result.id]); + const obj = Object.assign({ + dist: result.dist, + displayPath: pathSplitter(res[0]), + }, this.searchIndex[result.id]); // To be sure than it some items aren't considered as duplicate. obj.fullPath = res[2] + "|" + obj.ty; @@ -2076,6 +2144,11 @@ class DocSearch { duplicates.add(obj.fullPath); duplicates.add(res[2]); + if (typeInfo !== null) { + obj.displayTypeSignature = + this.formatDisplayTypeSignature(obj, typeInfo); + } + obj.href = res[1]; out.push(obj); if (out.length >= MAX_RESULTS) { @@ -2086,6 +2159,327 @@ class DocSearch { return out; }; + /** + * Add extra data to result objects, and filter items that have been + * marked for removal. + * + * The output is formatted as an array of hunks, where odd numbered + * hunks are highlighted and even numbered ones are not. + * + * @param {ResultObject} obj + * @param {"sig"|"elems"|"returned"|null} typeInfo + * @param {ParsedQuery} query + * @returns Promise< + * "type": Array, + * "mappedNames": Map, + * "whereClause": Map>, + * > + */ + this.formatDisplayTypeSignature = async(obj, typeInfo) => { + let fnInputs = null; + let fnOutput = null; + let mgens = null; + if (typeInfo !== "elems" && typeInfo !== "returned") { + fnInputs = unifyFunctionTypes( + obj.type.inputs, + parsedQuery.elems, + obj.type.where_clause, + null, + mgensScratch => { + fnOutput = unifyFunctionTypes( + obj.type.output, + parsedQuery.returned, + obj.type.where_clause, + mgensScratch, + mgensOut => { + mgens = mgensOut; + return true; + }, + 0, + ); + return !!fnOutput; + }, + 0, + ); + } else { + const arr = typeInfo === "elems" ? obj.type.inputs : obj.type.output; + const highlighted = unifyFunctionTypes( + arr, + parsedQuery.elems, + obj.type.where_clause, + null, + mgensOut => { + mgens = mgensOut; + return true; + }, + 0, + ); + if (typeInfo === "elems") { + fnInputs = highlighted; + } else { + fnOutput = highlighted; + } + } + if (!fnInputs) { + fnInputs = obj.type.inputs; + } + if (!fnOutput) { + fnOutput = obj.type.output; + } + const mappedNames = new Map(); + const whereClause = new Map(); + + const fnParamNames = obj.paramNames; + const queryParamNames = []; + /** + * Recursively writes a map of IDs to query generic names, + * which are later used to map query generic names to function generic names. + * For example, when the user writes `X -> Option` and the function + * is actually written as `T -> Option`, this function stores the + * mapping `(-1, "X")`, and the writeFn function looks up the entry + * for -1 to form the final, user-visible mapping of "X is T". + * + * @param {QueryElement} queryElem + */ + const remapQuery = queryElem => { + if (queryElem.id < 0) { + queryParamNames[-1 - queryElem.id] = queryElem.name; + } + if (queryElem.generics.length > 0) { + queryElem.generics.forEach(remapQuery); + } + if (queryElem.bindings.size > 0) { + [...queryElem.bindings.values()].flat().forEach(remapQuery); + } + }; + + parsedQuery.elems.forEach(remapQuery); + parsedQuery.returned.forEach(remapQuery); + + /** + * Write text to a highlighting array. + * Index 0 is not highlighted, index 1 is highlighted, + * index 2 is not highlighted, etc. + * + * @param {{name: string, highlighted: bool|undefined}} fnType - input + * @param {[string]} result + */ + const pushText = (fnType, result) => { + // If !!(result.length % 2) == false, then pushing a new slot starts an even + // numbered slot. Even numbered slots are not highlighted. + // + // `highlighted` will not be defined if an entire subtree is not highlighted, + // so `!!` is used to coerce it to boolean. `result.length % 2` is used to + // check if the number is even, but it evaluates to a number, so it also + // needs coerced to a boolean. + if (!!(result.length % 2) === !!fnType.highlighted) { + result.push(""); + } else if (result.length === 0 && !!fnType.highlighted) { + result.push(""); + result.push(""); + } + + result[result.length - 1] += fnType.name; + }; + + /** + * Write a higher order function type: either a function pointer + * or a trait bound on Fn, FnMut, or FnOnce. + * + * @param {FunctionType} fnType - input + * @param {[string]} result + */ + const writeHof = (fnType, result) => { + const hofOutput = fnType.bindings.get(this.typeNameIdOfOutput) || []; + const hofInputs = fnType.generics; + pushText(fnType, result); + pushText({name: " (", highlighted: false}, result); + let needsComma = false; + for (const fnType of hofInputs) { + if (needsComma) { + pushText({ name: ", ", highlighted: false }, result); + } + needsComma = true; + writeFn(fnType, result); + } + pushText({ + name: hofOutput.length === 0 ? ")" : ") -> ", + highlighted: false, + }, result); + if (hofOutput.length > 1) { + pushText({name: "(", highlighted: false}, result); + } + needsComma = false; + for (const fnType of hofOutput) { + if (needsComma) { + pushText({ name: ", ", highlighted: false }, result); + } + needsComma = true; + writeFn(fnType, result); + } + if (hofOutput.length > 1) { + pushText({name: ")", highlighted: false}, result); + } + }; + + /** + * Write a primitive type with special syntax, like `!` or `[T]`. + * Returns `false` if the supplied type isn't special. + * + * @param {FunctionType} fnType + * @param {[string]} result + */ + const writeSpecialPrimitive = (fnType, result) => { + if (fnType.id === this.typeNameIdOfArray || fnType.id === this.typeNameIdOfSlice || + fnType.id === this.typeNameIdOfTuple || fnType.id === this.typeNameIdOfUnit) { + const [ob, sb] = + fnType.id === this.typeNameIdOfArray || + fnType.id === this.typeNameIdOfSlice ? + ["[", "]"] : + ["(", ")"]; + pushText({ name: ob, highlighted: fnType.highlighted }, result); + onEachBtwn( + fnType.generics, + nested => writeFn(nested, result), + () => pushText({ name: ", ", highlighted: false }, result), + ); + pushText({ name: sb, highlighted: fnType.highlighted }, result); + return true; + } else if (fnType.id === this.typeNameIdOfReference) { + pushText({ name: "&", highlighted: fnType.highlighted }, result); + let prevHighlighted = false; + onEachBtwn( + fnType.generics, + value => { + prevHighlighted = value.highlighted; + writeFn(value, result); + }, + value => pushText({ + name: " ", + highlighted: prevHighlighted && value.highlighted, + }, result), + ); + return true; + } else if (fnType.id === this.typeNameIdOfFn) { + writeHof(fnType, result); + return true; + } + return false; + }; + /** + * Write a type. This function checks for special types, + * like slices, with their own formatting. It also handles + * updating the where clause and generic type param map. + * + * @param {FunctionType} fnType + * @param {[string]} result + */ + const writeFn = (fnType, result) => { + if (fnType.id < 0) { + const queryId = mgens && mgens.has(fnType.id) ? mgens.get(fnType.id) : null; + if (fnParamNames[-1 - fnType.id] === "") { + for (const nested of fnType.generics) { + writeFn(nested, result); + } + return; + } else if (queryId < 0) { + mappedNames.set( + fnParamNames[-1 - fnType.id], + queryParamNames[-1 - queryId], + ); + } + pushText({ + name: fnParamNames[-1 - fnType.id], + highlighted: !!fnType.highlighted, + }, result); + const where = []; + onEachBtwn( + fnType.generics, + nested => writeFn(nested, where), + () => pushText({ name: " + ", highlighted: false }, where), + ); + if (where.length > 0) { + whereClause.set(fnParamNames[-1 - fnType.id], where); + } + } else { + if (fnType.ty === TY_PRIMITIVE) { + if (writeSpecialPrimitive(fnType, result)) { + return; + } + } else if (fnType.ty === TY_TRAIT && ( + fnType.id === this.typeNameIdOfFn || + fnType.id === this.typeNameIdOfFnMut || + fnType.id === this.typeNameIdOfFnOnce)) { + writeHof(fnType, result); + return; + } + pushText(fnType, result); + let hasBindings = false; + if (fnType.bindings.size > 0) { + onEachBtwn( + fnType.bindings, + ([key, values]) => { + const name = this.assocTypeIdNameMap.get(key); + if (values.length === 1 && values[0].id < 0 && + `${fnType.name}::${name}` === fnParamNames[-1 - values[0].id]) { + // the internal `Item=Iterator::Item` type variable should be + // shown in the where clause and name mapping output, but is + // redundant in this spot + for (const value of values) { + writeFn(value, []); + } + return true; + } + if (!hasBindings) { + hasBindings = true; + pushText({ name: "<", highlighted: false }, result); + } + pushText({ name, highlighted: false }, result); + pushText({ + name: values.length > 1 ? "=(" : "=", + highlighted: false, + }, result); + onEachBtwn( + values, + value => writeFn(value, result), + () => pushText({ name: " + ", highlighted: false }, result), + ); + if (values.length > 1) { + pushText({ name: ")", highlighted: false }, result); + } + }, + () => pushText({ name: ", ", highlighted: false }, result), + ); + } + if (fnType.generics.length > 0) { + pushText({ name: hasBindings ? ", " : "<", highlighted: false }, result); + } + onEachBtwn( + fnType.generics, + value => writeFn(value, result), + () => pushText({ name: ", ", highlighted: false }, result), + ); + if (hasBindings || fnType.generics.length > 0) { + pushText({ name: ">", highlighted: false }, result); + } + } + }; + const type = []; + onEachBtwn( + fnInputs, + fnType => writeFn(fnType, type), + () => pushText({ name: ", ", highlighted: false }, type), + ); + pushText({ name: " -> ", highlighted: false }, type); + onEachBtwn( + fnOutput, + fnType => writeFn(fnType, type), + () => pushText({ name: ", ", highlighted: false }, type), + ); + + return {type, mappedNames, whereClause}; + }; + /** * This function takes a result map, and sorts it by various criteria, including edit * distance, substring match, and the crate it comes from. @@ -2095,9 +2489,9 @@ class DocSearch { * @param {string} preferredCrate * @returns {Promise<[ResultObject]>} */ - const sortResults = async(results, isType, preferredCrate) => { + const sortResults = async(results, typeInfo, preferredCrate) => { const userQuery = parsedQuery.userQuery; - const casedUserQuery = parsedQuery.original; + const normalizedUserQuery = parsedQuery.userQuery.toLowerCase(); const result_list = []; for (const result of results.values()) { result.item = this.searchIndex[result.id]; @@ -2109,15 +2503,15 @@ class DocSearch { let a, b; // sort by exact case-sensitive match - a = (aaa.item.name !== casedUserQuery); - b = (bbb.item.name !== casedUserQuery); + a = (aaa.item.name !== userQuery); + b = (bbb.item.name !== userQuery); if (a !== b) { return a - b; } // sort by exact match with regard to the last word (mismatch goes later) - a = (aaa.word !== userQuery); - b = (bbb.word !== userQuery); + a = (aaa.word !== normalizedUserQuery); + b = (bbb.word !== normalizedUserQuery); if (a !== b) { return a - b; } @@ -2205,17 +2599,17 @@ class DocSearch { return 0; }); - return transformResults(result_list); + return transformResults(result_list, typeInfo); }; /** * This function checks if a list of search query `queryElems` can all be found in the * search index (`fnTypes`). * - * This function returns `true` on a match, or `false` if none. If `solutionCb` is + * This function returns highlighted results on a match, or `null`. If `solutionCb` is * supplied, it will call that function with mgens, and that callback can accept or - * reject the result bu returning `true` or `false`. If the callback returns false, - * then this function will try with a different solution, or bail with false if it + * reject the result by returning `true` or `false`. If the callback returns false, + * then this function will try with a different solution, or bail with null if it * runs out of candidates. * * @param {Array} fnTypesIn - The objects to check. @@ -2228,7 +2622,7 @@ class DocSearch { * - Limit checks that Ty matches Vec, * but not Vec>>>> * - * @return {boolean} - Returns true if a match, false otherwise. + * @return {[FunctionType]|null} - Returns highlighed results if a match, null otherwise. */ function unifyFunctionTypes( fnTypesIn, @@ -2239,17 +2633,17 @@ class DocSearch { unboxingDepth, ) { if (unboxingDepth >= UNBOXING_LIMIT) { - return false; + return null; } /** * @type Map|null */ const mgens = mgensIn === null ? null : new Map(mgensIn); if (queryElems.length === 0) { - return !solutionCb || solutionCb(mgens); + return (!solutionCb || solutionCb(mgens)) ? fnTypesIn : null; } if (!fnTypesIn || fnTypesIn.length === 0) { - return false; + return null; } const ql = queryElems.length; const fl = fnTypesIn.length; @@ -2258,7 +2652,7 @@ class DocSearch { if (ql === 1 && queryElems[0].generics.length === 0 && queryElems[0].bindings.size === 0) { const queryElem = queryElems[0]; - for (const fnType of fnTypesIn) { + for (const [i, fnType] of fnTypesIn.entries()) { if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { continue; } @@ -2270,14 +2664,33 @@ class DocSearch { const mgensScratch = new Map(mgens); mgensScratch.set(fnType.id, queryElem.id); if (!solutionCb || solutionCb(mgensScratch)) { - return true; + const highlighted = [...fnTypesIn]; + highlighted[i] = Object.assign({ + highlighted: true, + }, fnType, { + generics: whereClause[-1 - fnType.id], + }); + return highlighted; } } else if (!solutionCb || solutionCb(mgens ? new Map(mgens) : null)) { // unifyFunctionTypeIsMatchCandidate already checks that ids match - return true; + const highlighted = [...fnTypesIn]; + highlighted[i] = Object.assign({ + highlighted: true, + }, fnType, { + generics: unifyFunctionTypes( + fnType.generics, + queryElem.generics, + whereClause, + mgens ? new Map(mgens) : null, + solutionCb, + unboxingDepth, + ) || fnType.generics, + }); + return highlighted; } } - for (const fnType of fnTypesIn) { + for (const [i, fnType] of fnTypesIn.entries()) { if (!unifyFunctionTypeIsUnboxCandidate( fnType, queryElem, @@ -2294,25 +2707,42 @@ class DocSearch { } const mgensScratch = new Map(mgens); mgensScratch.set(fnType.id, 0); - if (unifyFunctionTypes( + const highlightedGenerics = unifyFunctionTypes( whereClause[(-fnType.id) - 1], queryElems, whereClause, mgensScratch, solutionCb, unboxingDepth + 1, - )) { - return true; + ); + if (highlightedGenerics) { + const highlighted = [...fnTypesIn]; + highlighted[i] = Object.assign({ + highlighted: true, + }, fnType, { + generics: highlightedGenerics, + }); + return highlighted; + } + } else { + const highlightedGenerics = unifyFunctionTypes( + [...Array.from(fnType.bindings.values()).flat(), ...fnType.generics], + queryElems, + whereClause, + mgens ? new Map(mgens) : null, + solutionCb, + unboxingDepth + 1, + ); + if (highlightedGenerics) { + const highlighted = [...fnTypesIn]; + highlighted[i] = Object.assign({}, fnType, { + generics: highlightedGenerics, + bindings: new Map([...fnType.bindings.entries()].map(([k, v]) => { + return [k, highlightedGenerics.splice(0, v.length)]; + })), + }); + return highlighted; } - } else if (unifyFunctionTypes( - [...fnType.generics, ...Array.from(fnType.bindings.values()).flat()], - queryElems, - whereClause, - mgens ? new Map(mgens) : null, - solutionCb, - unboxingDepth + 1, - )) { - return true; } } return false; @@ -2369,6 +2799,8 @@ class DocSearch { if (!queryElemsTmp) { queryElemsTmp = queryElems.slice(0, qlast); } + let unifiedGenerics = []; + let unifiedGenericsMgens = null; const passesUnification = unifyFunctionTypes( fnTypes, queryElemsTmp, @@ -2391,7 +2823,7 @@ class DocSearch { } const simplifiedGenerics = solution.simplifiedGenerics; for (const simplifiedMgens of solution.mgens) { - const passesUnification = unifyFunctionTypes( + unifiedGenerics = unifyFunctionTypes( simplifiedGenerics, queryElem.generics, whereClause, @@ -2399,7 +2831,8 @@ class DocSearch { solutionCb, unboxingDepth, ); - if (passesUnification) { + if (unifiedGenerics) { + unifiedGenericsMgens = simplifiedMgens; return true; } } @@ -2408,7 +2841,23 @@ class DocSearch { unboxingDepth, ); if (passesUnification) { - return true; + passesUnification.length = fl; + passesUnification[flast] = passesUnification[i]; + passesUnification[i] = Object.assign({}, fnType, { + highlighted: true, + generics: unifiedGenerics, + bindings: new Map([...fnType.bindings.entries()].map(([k, v]) => { + return [k, queryElem.bindings.has(k) ? unifyFunctionTypes( + v, + queryElem.bindings.get(k), + whereClause, + unifiedGenericsMgens, + solutionCb, + unboxingDepth, + ) : unifiedGenerics.splice(0, v.length)]; + })), + }); + return passesUnification; } // backtrack fnTypes[flast] = fnTypes[i]; @@ -2443,7 +2892,7 @@ class DocSearch { Array.from(fnType.bindings.values()).flat() : []; const passesUnification = unifyFunctionTypes( - fnTypes.toSpliced(i, 1, ...generics, ...bindings), + fnTypes.toSpliced(i, 1, ...bindings, ...generics), queryElems, whereClause, mgensScratch, @@ -2451,10 +2900,24 @@ class DocSearch { unboxingDepth + 1, ); if (passesUnification) { - return true; + const highlightedGenerics = passesUnification.slice( + i, + i + generics.length + bindings.length, + ); + const highlightedFnType = Object.assign({}, fnType, { + generics: highlightedGenerics, + bindings: new Map([...fnType.bindings.entries()].map(([k, v]) => { + return [k, highlightedGenerics.splice(0, v.length)]; + })), + }); + return passesUnification.toSpliced( + i, + generics.length + bindings.length, + highlightedFnType, + ); } } - return false; + return null; } /** * Check if this function is a match candidate. @@ -2625,7 +3088,7 @@ class DocSearch { } }); if (simplifiedGenerics.length > 0) { - simplifiedGenerics = [...simplifiedGenerics, ...binds]; + simplifiedGenerics = [...binds, ...simplifiedGenerics]; } else { simplifiedGenerics = binds; } @@ -2727,33 +3190,18 @@ class DocSearch { if (unboxingDepth >= UNBOXING_LIMIT) { return false; } - if (row.bindings.size === 0 && elem.bindings.size === 0) { - if (elem.id < 0 && mgens === null) { - return row.id < 0 || checkIfInList( - row.generics, - elem, - whereClause, - mgens, - unboxingDepth + 1, - ); - } - if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && - typePassesFilter(elem.typeFilter, row.ty) && elem.generics.length === 0 && - // special case - elem.id !== this.typeNameIdOfArrayOrSlice - && elem.id !== this.typeNameIdOfTupleOrUnit - && elem.id !== this.typeNameIdOfHof - ) { - return row.id === elem.id || checkIfInList( - row.generics, - elem, - whereClause, - mgens, - unboxingDepth, - ); - } + if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && + row.generics.length === 0 && elem.generics.length === 0 && + row.bindings.size === 0 && elem.bindings.size === 0 && + // special case + elem.id !== this.typeNameIdOfArrayOrSlice && + elem.id !== this.typeNameIdOfHof && + elem.id !== this.typeNameIdOfTupleOrUnit + ) { + return row.id === elem.id && typePassesFilter(elem.typeFilter, row.ty); + } else { + return unifyFunctionTypes([row], [elem], whereClause, mgens, null, unboxingDepth); } - return unifyFunctionTypes([row], [elem], whereClause, mgens, null, unboxingDepth); }; /** @@ -3178,21 +3626,25 @@ class DocSearch { if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1 && elem.generics.length === 0 && elem.bindings.size === 0) || elem.typeFilter === TY_GENERIC) { - if (genericSymbols.has(elem.name)) { - elem.id = genericSymbols.get(elem.name); + if (genericSymbols.has(elem.normalizedPathLast)) { + elem.id = genericSymbols.get(elem.normalizedPathLast); } else { elem.id = -(genericSymbols.size + 1); - genericSymbols.set(elem.name, elem.id); + genericSymbols.set(elem.normalizedPathLast, elem.id); } - if (elem.typeFilter === -1 && elem.name.length >= 3) { + if (elem.typeFilter === -1 && elem.normalizedPathLast.length >= 3) { // Silly heuristic to catch if the user probably meant // to not write a generic parameter. We don't use it, // just bring it up. - const maxPartDistance = Math.floor(elem.name.length / 3); + const maxPartDistance = Math.floor(elem.normalizedPathLast.length / 3); let matchDist = maxPartDistance + 1; let matchName = ""; for (const name of this.typeNameIdMap.keys()) { - const dist = editDistance(name, elem.name, maxPartDistance); + const dist = editDistance( + name, + elem.normalizedPathLast, + maxPartDistance, + ); if (dist <= matchDist && dist <= maxPartDistance) { if (dist === matchDist && matchName > name) { continue; @@ -3294,17 +3746,18 @@ class DocSearch { innerRunQuery(); } + const isType = parsedQuery.foundElems !== 1 || parsedQuery.hasReturnArrow; const [sorted_in_args, sorted_returned, sorted_others] = await Promise.all([ - sortResults(results_in_args, true, currentCrate), - sortResults(results_returned, true, currentCrate), - sortResults(results_others, false, currentCrate), + sortResults(results_in_args, "elems", currentCrate), + sortResults(results_returned, "returned", currentCrate), + sortResults(results_others, (isType ? "query" : null), currentCrate), ]); const ret = createQueryResults( sorted_in_args, sorted_returned, sorted_others, parsedQuery); - await handleAliases(ret, parsedQuery.original.replace(/"/g, ""), + await handleAliases(ret, parsedQuery.userQuery.replace(/"/g, ""), filterCrates, currentCrate); await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => { const descs = await Promise.all(list.map(result => { @@ -3324,6 +3777,7 @@ class DocSearch { } } + // ==================== Core search logic end ==================== let rawSearchIndex; @@ -3455,15 +3909,18 @@ function focusSearchResult() { * @param {Array} array - The search results for this tab * @param {ParsedQuery} query * @param {boolean} display - True if this is the active tab + * @param {"sig"|"elems"|"returned"|null} typeInfo */ async function addTab(array, query, display) { const extraClass = display ? " active" : ""; - const output = document.createElement("div"); + const output = document.createElement( + array.length === 0 && query.error === null ? "div" : "ul", + ); if (array.length > 0) { output.className = "search-results " + extraClass; - for (const item of array) { + const lis = Promise.all(array.map(async item => { const name = item.name; const type = itemTypes[item.ty]; const longType = longItemTypes[item.ty]; @@ -3473,7 +3930,7 @@ async function addTab(array, query, display) { link.className = "result-" + type; link.href = item.href; - const resultName = document.createElement("div"); + const resultName = document.createElement("span"); resultName.className = "result-name"; resultName.insertAdjacentHTML( @@ -3496,10 +3953,73 @@ ${item.displayPath}${name}\ const description = document.createElement("div"); description.className = "desc"; description.insertAdjacentHTML("beforeend", item.desc); + if (item.displayTypeSignature) { + const {type, mappedNames, whereClause} = await item.displayTypeSignature; + const displayType = document.createElement("div"); + type.forEach((value, index) => { + if (index % 2 !== 0) { + const highlight = document.createElement("strong"); + highlight.appendChild(document.createTextNode(value)); + displayType.appendChild(highlight); + } else { + displayType.appendChild(document.createTextNode(value)); + } + }); + if (mappedNames.size > 0 || whereClause.size > 0) { + let addWhereLineFn = () => { + const line = document.createElement("div"); + line.className = "where"; + line.appendChild(document.createTextNode("where")); + displayType.appendChild(line); + addWhereLineFn = () => {}; + }; + for (const [name, qname] of mappedNames) { + // don't care unless the generic name is different + if (name === qname) { + continue; + } + addWhereLineFn(); + const line = document.createElement("div"); + line.className = "where"; + line.appendChild(document.createTextNode(` ${qname} matches `)); + const lineStrong = document.createElement("strong"); + lineStrong.appendChild(document.createTextNode(name)); + line.appendChild(lineStrong); + displayType.appendChild(line); + } + for (const [name, innerType] of whereClause) { + // don't care unless there's at least one highlighted entry + if (innerType.length <= 1) { + continue; + } + addWhereLineFn(); + const line = document.createElement("div"); + line.className = "where"; + line.appendChild(document.createTextNode(` ${name}: `)); + innerType.forEach((value, index) => { + if (index % 2 !== 0) { + const highlight = document.createElement("strong"); + highlight.appendChild(document.createTextNode(value)); + line.appendChild(highlight); + } else { + line.appendChild(document.createTextNode(value)); + } + }); + displayType.appendChild(line); + } + } + displayType.className = "type-signature"; + link.appendChild(displayType); + } link.appendChild(description); - output.appendChild(link); - } + return link; + })); + lis.then(lis => { + for (const li of lis) { + output.appendChild(li); + } + }); } else if (query.error === null) { output.className = "search-failed" + extraClass; output.innerHTML = "No results :(
" + @@ -3516,7 +4036,7 @@ ${item.displayPath}${name}\ "href=\"https://docs.rs\">Docs.rs for documentation of crates released on" + " crates.io."; } - return [output, array.length]; + return output; } function makeTabHeader(tabNb, text, nbElems) { @@ -3573,24 +4093,18 @@ async function showResults(results, go_to_first, filterCrates) { currentResults = results.query.userQuery; - const [ret_others, ret_in_args, ret_returned] = await Promise.all([ - addTab(results.others, results.query, true), - addTab(results.in_args, results.query, false), - addTab(results.returned, results.query, false), - ]); - // Navigate to the relevant tab if the current tab is empty, like in case users search // for "-> String". If they had selected another tab previously, they have to click on // it again. let currentTab = searchState.currentTab; - if ((currentTab === 0 && ret_others[1] === 0) || - (currentTab === 1 && ret_in_args[1] === 0) || - (currentTab === 2 && ret_returned[1] === 0)) { - if (ret_others[1] !== 0) { + if ((currentTab === 0 && results.others.length === 0) || + (currentTab === 1 && results.in_args.length === 0) || + (currentTab === 2 && results.returned.length === 0)) { + if (results.others.length !== 0) { currentTab = 0; - } else if (ret_in_args[1] !== 0) { + } else if (results.in_args.length) { currentTab = 1; - } else if (ret_returned[1] !== 0) { + } else if (results.returned.length) { currentTab = 2; } } @@ -3619,14 +4133,14 @@ async function showResults(results, go_to_first, filterCrates) { }); output += `

Query parser error: "${error.join("")}".

`; output += "
" + - makeTabHeader(0, "In Names", ret_others[1]) + + makeTabHeader(0, "In Names", results.others.length) + "
"; currentTab = 0; } else if (results.query.foundElems <= 1 && results.query.returned.length === 0) { output += "
" + - makeTabHeader(0, "In Names", ret_others[1]) + - makeTabHeader(1, "In Parameters", ret_in_args[1]) + - makeTabHeader(2, "In Return Types", ret_returned[1]) + + makeTabHeader(0, "In Names", results.others.length) + + makeTabHeader(1, "In Parameters", results.in_args.length) + + makeTabHeader(2, "In Return Types", results.returned.length) + "
"; } else { const signatureTabTitle = @@ -3634,7 +4148,7 @@ async function showResults(results, go_to_first, filterCrates) { results.query.returned.length === 0 ? "In Function Parameters" : "In Function Signatures"; output += "
" + - makeTabHeader(0, signatureTabTitle, ret_others[1]) + + makeTabHeader(0, signatureTabTitle, results.others.length) + "
"; currentTab = 0; } @@ -3656,11 +4170,17 @@ async function showResults(results, go_to_first, filterCrates) { `Consider searching for "${targ}" instead.`; } + const [ret_others, ret_in_args, ret_returned] = await Promise.all([ + addTab(results.others, results.query, currentTab === 0), + addTab(results.in_args, results.query, currentTab === 1), + addTab(results.returned, results.query, currentTab === 2), + ]); + const resultsElem = document.createElement("div"); resultsElem.id = "results"; - resultsElem.appendChild(ret_others[0]); - resultsElem.appendChild(ret_in_args[0]); - resultsElem.appendChild(ret_returned[0]); + resultsElem.appendChild(ret_others); + resultsElem.appendChild(ret_in_args); + resultsElem.appendChild(ret_returned); search.innerHTML = output; if (searchState.rustdocToolbar) { @@ -3724,11 +4244,11 @@ async function search(forced) { } // Update document title to maintain a meaningful browser history - searchState.title = "\"" + query.original + "\" Search - Rust"; + searchState.title = "\"" + query.userQuery + "\" Search - Rust"; // Because searching is incremental by character, only the most // recent search query is added to the browser history. - updateSearchHistory(buildUrl(query.original, filterCrates)); + updateSearchHistory(buildUrl(query.userQuery, filterCrates)); await showResults( await docSearch.execQuery(query, filterCrates, window.currentCrate), @@ -3942,4 +4462,3 @@ if (typeof window !== "undefined") { // exports. initSearch(new Map()); } -})(); diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index e162ba033cc55..7aa5e102e6d2a 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -2,6 +2,14 @@ const fs = require("fs"); const path = require("path"); + +function arrayToCode(array) { + return array.map((value, index) => { + value = value.split(" ").join(" "); + return (index % 2 === 1) ? ("`" + value + "`") : value; + }).join(""); +} + function loadContent(content) { const Module = module.constructor; const m = new Module(); @@ -84,7 +92,6 @@ function checkNeededFields(fullPath, expected, error_text, queryName, position) if (fullPath.length === 0) { fieldsToCheck = [ "foundElems", - "original", "returned", "userQuery", "error", @@ -181,15 +188,7 @@ function valueCheck(fullPath, expected, result, error_text, queryName) { if (!result_v.forEach) { throw result_v; } - result_v.forEach((value, index) => { - value = value.split(" ").join(" "); - if (index % 2 === 1) { - result_v[index] = "`" + value + "`"; - } else { - result_v[index] = value; - } - }); - result_v = result_v.join(""); + result_v = arrayToCode(result_v); } const obj_path = fullPath + (fullPath.length > 0 ? "." : "") + key; valueCheck(obj_path, expected[key], result_v, error_text, queryName); @@ -437,9 +436,41 @@ function loadSearchJS(doc_folder, resource_suffix) { searchModule.initSearch(searchIndex.searchIndex); const docSearch = searchModule.docSearch; return { - doSearch: function(queryStr, filterCrate, currentCrate) { - return docSearch.execQuery(searchModule.parseQuery(queryStr), + doSearch: async function(queryStr, filterCrate, currentCrate) { + const result = await docSearch.execQuery(searchModule.parseQuery(queryStr), filterCrate, currentCrate); + for (const tab in result) { + if (!Object.prototype.hasOwnProperty.call(result, tab)) { + continue; + } + if (!(result[tab] instanceof Array)) { + continue; + } + for (const entry of result[tab]) { + for (const key in entry) { + if (!Object.prototype.hasOwnProperty.call(entry, key)) { + continue; + } + if (key === "displayTypeSignature") { + const {type, mappedNames, whereClause} = + await entry.displayTypeSignature; + entry.displayType = arrayToCode(type); + entry.displayMappedNames = [...mappedNames.entries()] + .map(([name, qname]) => { + return `${name} = ${qname}`; + }).join(", "); + entry.displayWhereClause = [...whereClause.entries()] + .flatMap(([name, value]) => { + if (value.length === 0) { + return []; + } + return [`${name}: ${arrayToCode(value)}`]; + }).join(", "); + } + } + } + } + return result; }, getCorrections: function(queryStr, filterCrate, currentCrate) { const parsedQuery = searchModule.parseQuery(queryStr); diff --git a/tests/rustdoc-gui/search-about-this-result.goml b/tests/rustdoc-gui/search-about-this-result.goml new file mode 100644 index 0000000000000..62780d01ed7e8 --- /dev/null +++ b/tests/rustdoc-gui/search-about-this-result.goml @@ -0,0 +1,42 @@ +// Check the "About this Result" popover. +// Try a complex result. +go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=scroll_traits::Iterator,(T->bool)->(Extend,Extend)" + +// These two commands are used to be sure the search will be run. +focus: ".search-input" +press-key: "Enter" + +wait-for: "#search-tabs" +assert-count: ("#search-tabs button", 1) +assert-count: (".search-results > a", 1) + +assert: "//div[@class='type-signature']/strong[text()='Iterator']" +assert: "//div[@class='type-signature']/strong[text()='(']" +assert: "//div[@class='type-signature']/strong[text()=')']" + +assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='FnMut']" +assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='Iterator::Item']" +assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='bool']" +assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='Extend']" + +assert-text: ("div.type-signature div.where:nth-child(4)", "where") +assert-text: ("div.type-signature div.where:nth-child(5)", " T matches Iterator::Item") +assert-text: ("div.type-signature div.where:nth-child(6)", " F: FnMut (&Iterator::Item) -> bool") +assert-text: ("div.type-signature div.where:nth-child(7)", " B: Default + Extend") + +// Try a simple result that *won't* give an info box. +go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=F->lib2::WhereWhitespace" + +// These two commands are used to be sure the search will be run. +focus: ".search-input" +press-key: "Enter" + +wait-for: "#search-tabs" +assert-text: ("//div[@class='type-signature']", "F -> WhereWhitespace") +assert-count: ("#search-tabs button", 1) +assert-count: (".search-results > a", 1) +assert-count: ("//div[@class='type-signature']/div[@class='where']", 0) + +assert: "//div[@class='type-signature']/strong[text()='F']" +assert: "//div[@class='type-signature']/strong[text()='WhereWhitespace']" +assert: "//div[@class='type-signature']/strong[text()='T']" diff --git a/tests/rustdoc-gui/search-corrections.goml b/tests/rustdoc-gui/search-corrections.goml index b81b1f382a957..f80675730c41a 100644 --- a/tests/rustdoc-gui/search-corrections.goml +++ b/tests/rustdoc-gui/search-corrections.goml @@ -24,7 +24,7 @@ assert-css: (".search-corrections", { }) assert-text: ( ".search-corrections", - "Type \"notablestructwithlongnamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead." + "Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead." ) // Corrections do get shown on the "In Return Type" tab. @@ -35,7 +35,7 @@ assert-css: (".search-corrections", { }) assert-text: ( ".search-corrections", - "Type \"notablestructwithlongnamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead." + "Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead." ) // Now, explicit return values @@ -52,7 +52,7 @@ assert-css: (".search-corrections", { }) assert-text: ( ".search-corrections", - "Type \"notablestructwithlongnamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead." + "Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead." ) // Now, generic correction @@ -69,7 +69,7 @@ assert-css: (".search-corrections", { }) assert-text: ( ".search-corrections", - "Type \"notablestructwithlongnamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead." + "Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead." ) // Now, generic correction plus error @@ -86,7 +86,7 @@ assert-css: (".search-corrections", { }) assert-text: ( ".search-corrections", - "Type \"notablestructwithlongnamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead." + "Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead." ) go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" @@ -102,5 +102,5 @@ assert-css: (".error", { }) assert-text: ( ".error", - "Query parser error: \"Generic type parameter notablestructwithlongnamr does not accept generic parameters\"." + "Query parser error: \"Generic type parameter NotableStructWithLongNamr does not accept generic parameters\"." ) diff --git a/tests/rustdoc-js-std/option-type-signatures.js b/tests/rustdoc-js-std/option-type-signatures.js index e154fa707ab3d..1690d5dc8b5bb 100644 --- a/tests/rustdoc-js-std/option-type-signatures.js +++ b/tests/rustdoc-js-std/option-type-signatures.js @@ -6,79 +6,217 @@ const EXPECTED = [ { 'query': 'option, fnonce -> option', 'others': [ - { 'path': 'std::option::Option', 'name': 'map' }, + { + 'path': 'std::option::Option', + 'name': 'map', + 'displayType': '`Option`, F -> `Option`', + 'displayWhereClause': "F: `FnOnce` (T) -> U", + }, + ], + }, + { + 'query': 'option, fnonce -> option', + 'others': [ + { + 'path': 'std::option::Option', + 'name': 'map', + 'displayType': '`Option`<`T`>, F -> `Option`', + 'displayWhereClause': "F: `FnOnce` (T) -> U", + }, ], }, { 'query': 'option -> default', 'others': [ - { 'path': 'std::option::Option', 'name': 'unwrap_or_default' }, - { 'path': 'std::option::Option', 'name': 'get_or_insert_default' }, + { + 'path': 'std::option::Option', + 'name': 'unwrap_or_default', + 'displayType': '`Option` -> `T`', + 'displayWhereClause': "T: `Default`", + }, + { + 'path': 'std::option::Option', + 'name': 'get_or_insert_default', + 'displayType': '&mut `Option` -> &mut `T`', + 'displayWhereClause': "T: `Default`", + }, ], }, { 'query': 'option -> []', 'others': [ - { 'path': 'std::option::Option', 'name': 'as_slice' }, - { 'path': 'std::option::Option', 'name': 'as_mut_slice' }, + { + 'path': 'std::option::Option', + 'name': 'as_slice', + 'displayType': '&`Option` -> &`[`T`]`', + }, + { + 'path': 'std::option::Option', + 'name': 'as_mut_slice', + 'displayType': '&mut `Option` -> &mut `[`T`]`', + }, ], }, { 'query': 'option, option -> option', 'others': [ - { 'path': 'std::option::Option', 'name': 'or' }, - { 'path': 'std::option::Option', 'name': 'xor' }, + { + 'path': 'std::option::Option', + 'name': 'or', + 'displayType': '`Option`<`T`>, `Option`<`T`> -> `Option`<`T`>', + }, + { + 'path': 'std::option::Option', + 'name': 'xor', + 'displayType': '`Option`<`T`>, `Option`<`T`> -> `Option`<`T`>', + }, ], }, { 'query': 'option, option -> option', 'others': [ - { 'path': 'std::option::Option', 'name': 'and' }, - { 'path': 'std::option::Option', 'name': 'zip' }, + { + 'path': 'std::option::Option', + 'name': 'and', + 'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<`U`>', + }, + { + 'path': 'std::option::Option', + 'name': 'zip', + 'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<(T, `U`)>', + }, ], }, { 'query': 'option, option -> option', 'others': [ - { 'path': 'std::option::Option', 'name': 'and' }, - { 'path': 'std::option::Option', 'name': 'zip' }, + { + 'path': 'std::option::Option', + 'name': 'and', + 'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<`U`>', + }, + { + 'path': 'std::option::Option', + 'name': 'zip', + 'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<(`T`, U)>', + }, ], }, { 'query': 'option, option -> option', 'others': [ - { 'path': 'std::option::Option', 'name': 'zip' }, + { + 'path': 'std::option::Option', + 'name': 'zip', + 'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<(`T`, `U`)>', + }, ], }, { 'query': 'option, e -> result', 'others': [ - { 'path': 'std::option::Option', 'name': 'ok_or' }, - { 'path': 'std::result::Result', 'name': 'transpose' }, + { + 'path': 'std::option::Option', + 'name': 'ok_or', + 'displayType': '`Option`<`T`>, `E` -> `Result`<`T`, `E`>', + }, + { + 'path': 'std::result::Result', + 'name': 'transpose', + 'displayType': 'Result<`Option`<`T`>, `E`> -> Option<`Result`<`T`, `E`>>', + }, ], }, { 'query': 'result, e> -> option>', 'others': [ - { 'path': 'std::result::Result', 'name': 'transpose' }, + { + 'path': 'std::result::Result', + 'name': 'transpose', + 'displayType': '`Result`<`Option`<`T`>, `E`> -> `Option`<`Result`<`T`, `E`>>', + }, ], }, { 'query': 'option, option -> bool', 'others': [ - { 'path': 'std::option::Option', 'name': 'eq' }, + { + 'path': 'std::option::Option', + 'name': 'eq', + 'displayType': '&`Option`<`T`>, &`Option`<`T`> -> `bool`', + }, ], }, { 'query': 'option> -> option', 'others': [ - { 'path': 'std::option::Option', 'name': 'flatten' }, + { + 'path': 'std::option::Option', + 'name': 'flatten', + 'displayType': '`Option`<`Option`<`T`>> -> `Option`<`T`>', + }, ], }, { 'query': 'option', 'returned': [ - { 'path': 'std::result::Result', 'name': 'ok' }, + { + 'path': 'std::result::Result', + 'name': 'ok', + 'displayType': 'Result -> `Option`<`T`>', + }, + ], + }, + { + 'query': 'option, (fnonce () -> u) -> option', + 'others': [ + { + 'path': 'std::option::Option', + 'name': 'map', + 'displayType': '`Option`<`T`>, F -> `Option`', + 'displayMappedNames': `T = t, U = u`, + 'displayWhereClause': "F: `FnOnce` (T) -> `U`", + }, + { + 'path': 'std::option::Option', + 'name': 'and_then', + 'displayType': '`Option`<`T`>, F -> `Option`', + 'displayMappedNames': `T = t, U = u`, + 'displayWhereClause': "F: `FnOnce` (T) -> Option<`U`>", + }, + { + 'path': 'std::option::Option', + 'name': 'zip_with', + 'displayType': 'Option, `Option`<`U`>, F -> `Option`', + 'displayMappedNames': `U = t, R = u`, + 'displayWhereClause': "F: `FnOnce` (T, U) -> `R`", + }, + { + 'path': 'std::task::Poll', + 'name': 'map_ok', + 'displayType': 'Poll<`Option`>>, F -> Poll<`Option`>>', + 'displayMappedNames': `T = t, U = u`, + 'displayWhereClause': "F: `FnOnce` (T) -> `U`", + }, + { + 'path': 'std::task::Poll', + 'name': 'map_err', + 'displayType': 'Poll<`Option`>>, F -> Poll<`Option`>>', + 'displayMappedNames': `T = t, U = u`, + 'displayWhereClause': "F: `FnOnce` (E) -> `U`", + }, + ], + }, + { + 'query': 'option, (fnonce () -> option) -> option', + 'others': [ + { + 'path': 'std::option::Option', + 'name': 'and_then', + 'displayType': '`Option`<`T`>, F -> `Option`', + 'displayMappedNames': `T = t, U = u`, + 'displayWhereClause': "F: `FnOnce` (T) -> `Option`<`U`>", + }, ], }, ]; diff --git a/tests/rustdoc-js-std/parser-bindings.js b/tests/rustdoc-js-std/parser-bindings.js index c4909c6242d75..bd379f139ffa2 100644 --- a/tests/rustdoc-js-std/parser-bindings.js +++ b/tests/rustdoc-js-std/parser-bindings.js @@ -3,20 +3,22 @@ const PARSED = [ query: 'A', elems: [ { - name: "a", + name: "A", fullPath: ["a"], pathWithoutLast: [], pathLast: "a", + normalizedPathLast: "a", generics: [], bindings: [ [ 'b', [ { - name: "c", + name: "C", fullPath: ["c"], pathWithoutLast: [], pathLast: "c", + normalizedPathLast: "c", generics: [], typeFilter: -1, }, @@ -27,16 +29,15 @@ const PARSED = [ }, ], foundElems: 1, - original: 'A', + userQuery: 'A', returned: [], - userQuery: 'a', error: null, }, { query: 'A', elems: [ { - name: "a", + name: "A", fullPath: ["a"], pathWithoutLast: [], pathLast: "a", @@ -45,7 +46,7 @@ const PARSED = [ [ 'b', [{ - name: "c", + name: "C", fullPath: ["c"], pathWithoutLast: [], pathLast: "c", @@ -58,16 +59,15 @@ const PARSED = [ }, ], foundElems: 1, - original: 'A', + userQuery: 'A', returned: [], - userQuery: 'a', error: null, }, { query: 'A', elems: [ { - name: "a", + name: "A", fullPath: ["a"], pathWithoutLast: [], pathLast: "a", @@ -89,16 +89,15 @@ const PARSED = [ }, ], foundElems: 1, - original: 'A', + userQuery: 'A', returned: [], - userQuery: 'a', error: null, }, { query: 'A', elems: [ { - name: "a", + name: "A", fullPath: ["a"], pathWithoutLast: [], pathLast: "a", @@ -120,16 +119,15 @@ const PARSED = [ }, ], foundElems: 1, - original: 'A', + userQuery: 'A', returned: [], - userQuery: 'a', error: null, }, { query: 'A', elems: [ { - name: "a", + name: "A", fullPath: ["a"], pathWithoutLast: [], pathLast: "a", @@ -160,52 +158,47 @@ const PARSED = [ }, ], foundElems: 1, - original: 'A', + userQuery: 'A', returned: [], - userQuery: 'a', error: null, }, { query: 'A', elems: [], foundElems: 0, - original: 'A', + userQuery: 'A', returned: [], - userQuery: 'a', error: "Cannot write `=` twice in a binding", }, { query: 'A', elems: [], foundElems: 0, - original: 'A', + userQuery: 'A', returned: [], - userQuery: 'a', error: "Unexpected `>` after `=`", }, { query: 'B=C', elems: [], foundElems: 0, - original: 'B=C', + userQuery: 'B=C', returned: [], - userQuery: 'b=c', error: "Type parameter `=` must be within generics list", }, { query: '[B=C]', elems: [], foundElems: 0, - original: '[B=C]', + userQuery: '[B=C]', returned: [], - userQuery: '[b=c]', error: "Type parameter `=` cannot be within slice `[]`", }, { query: 'A=C>', elems: [ { - name: "a", + name: "A", fullPath: ["a"], pathWithoutLast: [], pathLast: "a", @@ -215,7 +208,7 @@ const PARSED = [ 'b', [ { - name: "c", + name: "C", fullPath: ["c"], pathWithoutLast: [], pathLast: "c", @@ -223,7 +216,7 @@ const PARSED = [ typeFilter: -1, }, { - name: "x", + name: "X", fullPath: ["x"], pathWithoutLast: [], pathLast: "x", @@ -237,9 +230,8 @@ const PARSED = [ }, ], foundElems: 1, - original: 'A=C>', + userQuery: 'A=C>', returned: [], - userQuery: 'a=c>', error: null, }, ]; diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index 5ce35bf511d75..068298e72360a 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -3,450 +3,400 @@ const PARSED = [ query: '

', elems: [], foundElems: 0, - original: "

", + userQuery: "

", returned: [], - userQuery: "

", error: "Found generics without a path", }, { query: '->

', elems: [], foundElems: 0, - original: "->

", + userQuery: "->

", returned: [], - userQuery: "->

", error: "Found generics without a path", }, { query: '-> *', elems: [], foundElems: 0, - original: "-> *", - returned: [], userQuery: "-> *", + returned: [], error: "Unexpected `*` after ` ` (not a valid identifier)", }, { query: 'a<"P">', elems: [], foundElems: 0, - original: "a<\"P\">", + userQuery: "a<\"P\">", returned: [], - userQuery: "a<\"p\">", error: "Unexpected `\"` in generics", }, { query: '"P" "P"', elems: [], foundElems: 0, - original: "\"P\" \"P\"", + userQuery: "\"P\" \"P\"", returned: [], - userQuery: "\"p\" \"p\"", error: "Cannot have more than one element if you use quotes", }, { query: '"P","P"', elems: [], foundElems: 0, - original: "\"P\",\"P\"", + userQuery: "\"P\",\"P\"", returned: [], - userQuery: "\"p\",\"p\"", error: "Cannot have more than one literal search element", }, { query: "P,\"P\"", elems: [], foundElems: 0, - original: "P,\"P\"", + userQuery: "P,\"P\"", returned: [], - userQuery: "p,\"p\"", error: "Cannot use literal search when there is more than one element", }, { query: '"p" p', elems: [], foundElems: 0, - original: "\"p\" p", - returned: [], userQuery: "\"p\" p", + returned: [], error: "Cannot have more than one element if you use quotes", }, { query: '"p",p', elems: [], foundElems: 0, - original: "\"p\",p", - returned: [], userQuery: "\"p\",p", + returned: [], error: "Cannot have more than one element if you use quotes", }, { query: '"const": p', elems: [], foundElems: 0, - original: "\"const\": p", - returned: [], userQuery: "\"const\": p", + returned: [], error: "Cannot use quotes on type filter", }, { query: "a<:a>", elems: [], foundElems: 0, - original: "a<:a>", - returned: [], userQuery: "a<:a>", + returned: [], error: "Expected type filter before `:`", }, { query: "a<::a>", elems: [], foundElems: 0, - original: "a<::a>", - returned: [], userQuery: "a<::a>", + returned: [], error: "Unexpected `::`: paths cannot start with `::`", }, { query: "(p -> p", elems: [], foundElems: 0, - original: "(p -> p", - returned: [], userQuery: "(p -> p", + returned: [], error: "Unclosed `(`", }, { query: "::a::b", elems: [], foundElems: 0, - original: "::a::b", - returned: [], userQuery: "::a::b", + returned: [], error: "Paths cannot start with `::`", }, { query: " ::a::b", elems: [], foundElems: 0, - original: "::a::b", - returned: [], userQuery: "::a::b", + returned: [], error: "Paths cannot start with `::`", }, { query: "a::::b", elems: [], foundElems: 0, - original: "a::::b", - returned: [], userQuery: "a::::b", + returned: [], error: "Unexpected `::::`", }, { query: "a:: ::b", elems: [], foundElems: 0, - original: "a:: ::b", - returned: [], userQuery: "a:: ::b", + returned: [], error: "Unexpected `:: ::`", }, { query: "a::\t::b", elems: [], foundElems: 0, - original: "a:: ::b", - returned: [], userQuery: "a:: ::b", + returned: [], error: "Unexpected `:: ::`", }, { query: "a::b::", elems: [], foundElems: 0, - original: "a::b::", - returned: [], userQuery: "a::b::", + returned: [], error: "Paths cannot end with `::`", }, { query: ":a", elems: [], foundElems: 0, - original: ":a", - returned: [], userQuery: ":a", + returned: [], error: "Expected type filter before `:`", }, { query: "a,b:", elems: [], foundElems: 0, - original: "a,b:", - returned: [], userQuery: "a,b:", + returned: [], error: "Unexpected `:` (expected path after type filter `b:`)", }, { query: "a (b:", elems: [], foundElems: 0, - original: "a (b:", - returned: [], userQuery: "a (b:", + returned: [], error: "Unclosed `(`", }, { query: "_:", elems: [], foundElems: 0, - original: "_:", - returned: [], userQuery: "_:", + returned: [], error: "Unexpected `_` (not a valid identifier)", }, { query: "ab:", elems: [], foundElems: 0, - original: "ab:", - returned: [], userQuery: "ab:", + returned: [], error: "Unexpected `:` (expected path after type filter `ab:`)", }, { query: "a:b", elems: [], foundElems: 0, - original: "a:b", - returned: [], userQuery: "a:b", + returned: [], error: "Unknown type filter `a`", }, { query: "a-bb", elems: [], foundElems: 0, - original: "a-bb", - returned: [], userQuery: "a-bb", + returned: [], error: "Unexpected `-` (did you mean `->`?)", }, { query: "a>bb", elems: [], foundElems: 0, - original: "a>bb", - returned: [], userQuery: "a>bb", + returned: [], error: "Unexpected `>` (did you mean `->`?)", }, { query: "ab'", elems: [], foundElems: 0, - original: "ab'", - returned: [], userQuery: "ab'", + returned: [], error: "Unexpected `'` after `b` (not a valid identifier)", }, { query: '"p" ', elems: [], foundElems: 0, - original: '"p" ', - returned: [], userQuery: '"p" ', + returned: [], error: "Cannot have more than one element if you use quotes", }, { query: '"p",', elems: [], foundElems: 0, - original: '"p",', - returned: [], userQuery: '"p",', + returned: [], error: "Found generics without a path", }, { query: '"p" a', elems: [], foundElems: 0, - original: '"p" a', - returned: [], userQuery: '"p" a', + returned: [], error: "Cannot have more than one element if you use quotes", }, { query: '"p",a', elems: [], foundElems: 0, - original: '"p",a', - returned: [], userQuery: '"p",a', + returned: [], error: "Cannot have more than one element if you use quotes", }, { query: "a,<", elems: [], foundElems: 0, - original: 'a,<', - returned: [], userQuery: 'a,<', + returned: [], error: 'Found generics without a path', }, { query: "aaaaa<>b", elems: [], foundElems: 0, - original: 'aaaaa<>b', - returned: [], userQuery: 'aaaaa<>b', + returned: [], error: 'Expected `,`, `:` or `->` after `>`, found `b`', }, { query: "fn:aaaaa<>b", elems: [], foundElems: 0, - original: 'fn:aaaaa<>b', - returned: [], userQuery: 'fn:aaaaa<>b', + returned: [], error: 'Expected `,`, `:` or `->` after `>`, found `b`', }, { query: "->a<>b", elems: [], foundElems: 0, - original: '->a<>b', - returned: [], userQuery: '->a<>b', + returned: [], error: 'Expected `,` or `=` after `>`, found `b`', }, { query: "a<->", elems: [], foundElems: 0, - original: 'a<->', - returned: [], userQuery: 'a<->', + returned: [], error: 'Unclosed `<`', }, { query: "a:", elems: [], foundElems: 0, - original: "a:", - returned: [], userQuery: "a:", + returned: [], error: 'Unexpected `<` in type filter (before `:`)', }, { query: "a<>:", elems: [], foundElems: 0, - original: "a<>:", - returned: [], userQuery: "a<>:", + returned: [], error: 'Unexpected `<` in type filter (before `:`)', }, { query: "a,:", elems: [], foundElems: 0, - original: "a,:", - returned: [], userQuery: "a,:", + returned: [], error: 'Expected type filter before `:`', }, { query: "a!:", elems: [], foundElems: 0, - original: "a!:", - returned: [], userQuery: "a!:", + returned: [], error: 'Unexpected `!` in type filter (before `:`)', }, { query: " a<> :", elems: [], foundElems: 0, - original: "a<> :", - returned: [], userQuery: "a<> :", + returned: [], error: 'Expected `,`, `:` or `->` after `>`, found `:`', }, { query: "mod : :", elems: [], foundElems: 0, - original: "mod : :", - returned: [], userQuery: "mod : :", + returned: [], error: 'Unexpected `:` (expected path after type filter `mod:`)', }, { query: "mod: :", elems: [], foundElems: 0, - original: "mod: :", - returned: [], userQuery: "mod: :", + returned: [], error: 'Unexpected `:` (expected path after type filter `mod:`)', }, { query: "a!a", elems: [], foundElems: 0, - original: "a!a", - returned: [], userQuery: "a!a", + returned: [], error: 'Unexpected `!`: it can only be at the end of an ident', }, { query: "a!!", elems: [], foundElems: 0, - original: "a!!", - returned: [], userQuery: "a!!", + returned: [], error: 'Cannot have more than one `!` in an ident', }, { query: "mod:a!", elems: [], foundElems: 0, - original: "mod:a!", - returned: [], userQuery: "mod:a!", + returned: [], error: 'Invalid search type: macro `!` and `mod` both specified', }, { query: "mod:!", elems: [], foundElems: 0, - original: "mod:!", - returned: [], userQuery: "mod:!", + returned: [], error: 'Invalid search type: primitive never type `!` and `mod` both specified', }, { query: "a!::a", elems: [], foundElems: 0, - original: "a!::a", - returned: [], userQuery: "a!::a", + returned: [], error: 'Cannot have associated items in macros', }, { query: "a<", elems: [], foundElems: 0, - original: "a<", - returned: [], userQuery: "a<", + returned: [], error: "Unclosed `<`", }, { @@ -479,9 +429,8 @@ const PARSED = [ }, ], foundElems: 2, - original: "p , y", - returned: [], userQuery: "p , y", + returned: [], error: null, }, { @@ -514,9 +463,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "p", - returned: [], userQuery: "p", + returned: [], error: null, }, { @@ -548,9 +496,8 @@ const PARSED = [ }, ], foundElems: 3, - original: "p ,x , y", - returned: [], userQuery: "p ,x , y", + returned: [], error: null, }, ]; diff --git a/tests/rustdoc-js-std/parser-filter.js b/tests/rustdoc-js-std/parser-filter.js index a1dd0ea3b5a99..cda950461f7e0 100644 --- a/tests/rustdoc-js-std/parser-filter.js +++ b/tests/rustdoc-js-std/parser-filter.js @@ -10,9 +10,8 @@ const PARSED = [ typeFilter: 7, }], foundElems: 1, - original: "fn:foo", - returned: [], userQuery: "fn:foo", + returned: [], error: null, }, { @@ -26,18 +25,16 @@ const PARSED = [ typeFilter: 6, }], foundElems: 1, - original: "enum : foo", - returned: [], userQuery: "enum : foo", + returned: [], error: null, }, { query: 'macro:foo', elems: [], foundElems: 0, - original: "macro:foo", - returned: [], userQuery: "macro:foo", + returned: [], error: "Unexpected `<` in type filter (before `:`)", }, { @@ -51,9 +48,8 @@ const PARSED = [ typeFilter: 16, }], foundElems: 1, - original: "macro!", - returned: [], userQuery: "macro!", + returned: [], error: null, }, { @@ -67,9 +63,8 @@ const PARSED = [ typeFilter: 16, }], foundElems: 1, - original: "macro:mac!", - returned: [], userQuery: "macro:mac!", + returned: [], error: null, }, { @@ -83,16 +78,15 @@ const PARSED = [ typeFilter: 16, }], foundElems: 1, - original: "a::mac!", - returned: [], userQuery: "a::mac!", + returned: [], error: null, }, { query: '-> fn:foo', elems: [], foundElems: 1, - original: "-> fn:foo", + userQuery: "-> fn:foo", returned: [{ name: "foo", fullPath: ["foo"], @@ -101,14 +95,13 @@ const PARSED = [ generics: [], typeFilter: 7, }], - userQuery: "-> fn:foo", error: null, }, { query: '-> fn:foo', elems: [], foundElems: 1, - original: "-> fn:foo", + userQuery: "-> fn:foo", returned: [{ name: "foo", fullPath: ["foo"], @@ -126,14 +119,13 @@ const PARSED = [ ], typeFilter: 7, }], - userQuery: "-> fn:foo", error: null, }, { query: '-> fn:foo', elems: [], foundElems: 1, - original: "-> fn:foo", + userQuery: "-> fn:foo", returned: [{ name: "foo", fullPath: ["foo"], @@ -159,7 +151,6 @@ const PARSED = [ ], typeFilter: 7, }], - userQuery: "-> fn:foo", error: null, }, ]; diff --git a/tests/rustdoc-js-std/parser-generics.js b/tests/rustdoc-js-std/parser-generics.js index 726ee56c2c1ba..8b8d95bcb8883 100644 --- a/tests/rustdoc-js-std/parser-generics.js +++ b/tests/rustdoc-js-std/parser-generics.js @@ -3,9 +3,8 @@ const PARSED = [ query: 'A, E>', elems: [], foundElems: 0, - original: 'A, E>', + userQuery: 'A, E>', returned: [], - userQuery: 'a, e>', error: 'Unclosed `<`', }, { @@ -29,9 +28,8 @@ const PARSED = [ }, ], foundElems: 2, - original: "p<>,u8", - returned: [], userQuery: "p<>,u8", + returned: [], error: null, }, { @@ -55,9 +53,8 @@ const PARSED = [ }, ], foundElems: 1, - original: '"p"', - returned: [], userQuery: '"p"', + returned: [], error: null, }, { @@ -89,9 +86,8 @@ const PARSED = [ }, ], foundElems: 1, - original: 'p>', - returned: [], userQuery: 'p>', + returned: [], error: null, }, { @@ -130,9 +126,8 @@ const PARSED = [ }, ], foundElems: 1, - original: 'p, r>', - returned: [], userQuery: 'p, r>', + returned: [], error: null, }, { @@ -171,9 +166,8 @@ const PARSED = [ }, ], foundElems: 1, - original: 'p>', - returned: [], userQuery: 'p>', + returned: [], error: null, }, ]; diff --git a/tests/rustdoc-js-std/parser-hof.js b/tests/rustdoc-js-std/parser-hof.js index 0b99c45b7a922..ca76101541280 100644 --- a/tests/rustdoc-js-std/parser-hof.js +++ b/tests/rustdoc-js-std/parser-hof.js @@ -12,13 +12,13 @@ const PARSED = [ [ "output", [{ - name: "f", + name: "F", fullPath: ["f"], pathWithoutLast: [], pathLast: "f", generics: [ { - name: "p", + name: "P", fullPath: ["p"], pathWithoutLast: [], pathLast: "p", @@ -32,9 +32,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "(-> F

)", + userQuery: "(-> F

)", returned: [], - userQuery: "(-> f

)", error: null, }, { @@ -49,7 +48,7 @@ const PARSED = [ [ "output", [{ - name: "p", + name: "P", fullPath: ["p"], pathWithoutLast: [], pathLast: "p", @@ -61,9 +60,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "(-> P)", + userQuery: "(-> P)", returned: [], - userQuery: "(-> p)", error: null, }, { @@ -90,9 +88,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "(->,a)", - returned: [], userQuery: "(->,a)", + returned: [], error: null, }, { @@ -103,13 +100,13 @@ const PARSED = [ pathWithoutLast: [], pathLast: "->", generics: [{ - name: "f", + name: "F", fullPath: ["f"], pathWithoutLast: [], pathLast: "f", generics: [ { - name: "p", + name: "P", fullPath: ["p"], pathWithoutLast: [], pathLast: "p", @@ -127,9 +124,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "(F

->)", + userQuery: "(F

->)", returned: [], - userQuery: "(f

->)", error: null, }, { @@ -140,7 +136,7 @@ const PARSED = [ pathWithoutLast: [], pathLast: "->", generics: [{ - name: "p", + name: "P", fullPath: ["p"], pathWithoutLast: [], pathLast: "p", @@ -156,9 +152,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "(P ->)", + userQuery: "(P ->)", returned: [], - userQuery: "(p ->)", error: null, }, { @@ -185,9 +180,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "(,a->)", - returned: [], userQuery: "(,a->)", + returned: [], error: null, }, { @@ -221,9 +215,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "(aaaaa->a)", - returned: [], userQuery: "(aaaaa->a)", + returned: [], error: null, }, { @@ -267,9 +260,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "(aaaaa, b -> a)", - returned: [], userQuery: "(aaaaa, b -> a)", + returned: [], error: null, }, { @@ -313,9 +305,8 @@ const PARSED = [ typeFilter: 1, }], foundElems: 1, - original: "primitive:(aaaaa, b -> a)", - returned: [], userQuery: "primitive:(aaaaa, b -> a)", + returned: [], error: null, }, { @@ -369,16 +360,15 @@ const PARSED = [ } ], foundElems: 2, - original: "x, trait:(aaaaa, b -> a)", - returned: [], userQuery: "x, trait:(aaaaa, b -> a)", + returned: [], error: null, }, // Rust-style HOF { query: "Fn () -> F

", elems: [{ - name: "fn", + name: "Fn", fullPath: ["fn"], pathWithoutLast: [], pathLast: "fn", @@ -387,13 +377,13 @@ const PARSED = [ [ "output", [{ - name: "f", + name: "F", fullPath: ["f"], pathWithoutLast: [], pathLast: "f", generics: [ { - name: "p", + name: "P", fullPath: ["p"], pathWithoutLast: [], pathLast: "p", @@ -407,15 +397,14 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "Fn () -> F

", + userQuery: "Fn () -> F

", returned: [], - userQuery: "fn () -> f

", error: null, }, { query: "FnMut() -> P", elems: [{ - name: "fnmut", + name: "FnMut", fullPath: ["fnmut"], pathWithoutLast: [], pathLast: "fnmut", @@ -424,7 +413,7 @@ const PARSED = [ [ "output", [{ - name: "p", + name: "P", fullPath: ["p"], pathWithoutLast: [], pathLast: "p", @@ -436,15 +425,14 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "FnMut() -> P", + userQuery: "FnMut() -> P", returned: [], - userQuery: "fnmut() -> p", error: null, }, { query: "(FnMut() -> P)", elems: [{ - name: "fnmut", + name: "FnMut", fullPath: ["fnmut"], pathWithoutLast: [], pathLast: "fnmut", @@ -453,7 +441,7 @@ const PARSED = [ [ "output", [{ - name: "p", + name: "P", fullPath: ["p"], pathWithoutLast: [], pathLast: "p", @@ -465,26 +453,25 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "(FnMut() -> P)", + userQuery: "(FnMut() -> P)", returned: [], - userQuery: "(fnmut() -> p)", error: null, }, { query: "Fn(F

)", elems: [{ - name: "fn", + name: "Fn", fullPath: ["fn"], pathWithoutLast: [], pathLast: "fn", generics: [{ - name: "f", + name: "F", fullPath: ["f"], pathWithoutLast: [], pathLast: "f", generics: [ { - name: "p", + name: "P", fullPath: ["p"], pathWithoutLast: [], pathLast: "p", @@ -502,9 +489,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "Fn(F

)", + userQuery: "Fn(F

)", returned: [], - userQuery: "fn(f

)", error: null, }, { @@ -548,9 +534,8 @@ const PARSED = [ typeFilter: 1, }], foundElems: 1, - original: "primitive:fnonce(aaaaa, b) -> a", - returned: [], userQuery: "primitive:fnonce(aaaaa, b) -> a", + returned: [], error: null, }, { @@ -594,9 +579,8 @@ const PARSED = [ typeFilter: 1, }], foundElems: 1, - original: "primitive:fnonce(aaaaa, keyword:b) -> trait:a", - returned: [], userQuery: "primitive:fnonce(aaaaa, keyword:b) -> trait:a", + returned: [], error: null, }, { @@ -665,9 +649,8 @@ const PARSED = [ } ], foundElems: 2, - original: "x, trait:fn(aaaaa, b -> a)", - returned: [], userQuery: "x, trait:fn(aaaaa, b -> a)", + returned: [], error: null, }, { @@ -704,9 +687,8 @@ const PARSED = [ } ], foundElems: 2, - original: "a,b(c)", - returned: [], userQuery: "a,b(c)", + returned: [], error: null, }, ]; diff --git a/tests/rustdoc-js-std/parser-ident.js b/tests/rustdoc-js-std/parser-ident.js index cc79c58f1da3a..f65391b157188 100644 --- a/tests/rustdoc-js-std/parser-ident.js +++ b/tests/rustdoc-js-std/parser-ident.js @@ -2,7 +2,7 @@ const PARSED = [ { query: "R", elems: [{ - name: "r", + name: "R", fullPath: ["r"], pathWithoutLast: [], pathLast: "r", @@ -19,9 +19,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "R", + userQuery: "R", returned: [], - userQuery: "r", error: null, }, { @@ -35,9 +34,8 @@ const PARSED = [ typeFilter: 1, }], foundElems: 1, - original: "!", - returned: [], userQuery: "!", + returned: [], error: null, }, { @@ -51,27 +49,24 @@ const PARSED = [ typeFilter: 16, }], foundElems: 1, - original: "a!", - returned: [], userQuery: "a!", + returned: [], error: null, }, { query: "a!::b", elems: [], foundElems: 0, - original: "a!::b", - returned: [], userQuery: "a!::b", + returned: [], error: "Cannot have associated items in macros", }, { query: "!", elems: [], foundElems: 0, - original: "!", + userQuery: "!", returned: [], - userQuery: "!", error: "Never type `!` does not accept generic parameters", }, { @@ -85,36 +80,32 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "!::b", - returned: [], userQuery: "!::b", + returned: [], error: null, }, { query: "b::!", elems: [], foundElems: 0, - original: "b::!", - returned: [], userQuery: "b::!", + returned: [], error: "Never type `!` is not associated item", }, { query: "!::!", elems: [], foundElems: 0, - original: "!::!", - returned: [], userQuery: "!::!", + returned: [], error: "Never type `!` is not associated item", }, { query: "b::!::c", elems: [], foundElems: 0, - original: "b::!::c", - returned: [], userQuery: "b::!::c", + returned: [], error: "Never type `!` is not associated item", }, { @@ -126,7 +117,7 @@ const PARSED = [ pathLast: "b", generics: [ { - name: "t", + name: "T", fullPath: ["t"], pathWithoutLast: [], pathLast: "t", @@ -137,18 +128,16 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "!::b", + userQuery: "!::b", returned: [], - userQuery: "!::b", error: null, }, { query: "a!::b!", elems: [], foundElems: 0, - original: "a!::b!", - returned: [], userQuery: "a!::b!", + returned: [], error: "Cannot have associated items in macros", }, ]; diff --git a/tests/rustdoc-js-std/parser-literal.js b/tests/rustdoc-js-std/parser-literal.js index 87c06224dbf2d..63e07a246a13d 100644 --- a/tests/rustdoc-js-std/parser-literal.js +++ b/tests/rustdoc-js-std/parser-literal.js @@ -2,13 +2,13 @@ const PARSED = [ { query: 'R

', elems: [{ - name: "r", + name: "R", fullPath: ["r"], pathWithoutLast: [], pathLast: "r", generics: [ { - name: "p", + name: "P", fullPath: ["p"], pathWithoutLast: [], pathLast: "p", @@ -18,9 +18,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "R

", + userQuery: "R

", returned: [], - userQuery: "r

", error: null, } ]; diff --git a/tests/rustdoc-js-std/parser-paths.js b/tests/rustdoc-js-std/parser-paths.js index 774e5d028cc2c..bb34e22e518a4 100644 --- a/tests/rustdoc-js-std/parser-paths.js +++ b/tests/rustdoc-js-std/parser-paths.js @@ -2,7 +2,7 @@ const PARSED = [ { query: 'A::B', elems: [{ - name: "a::b", + name: "A::B", fullPath: ["a", "b"], pathWithoutLast: ["a"], pathLast: "b", @@ -10,9 +10,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "A::B", + userQuery: "A::B", returned: [], - userQuery: "a::b", error: null, }, { @@ -26,9 +25,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: 'a:: a', - returned: [], userQuery: 'a:: a', + returned: [], error: null, }, { @@ -42,9 +40,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: 'a ::a', - returned: [], userQuery: 'a ::a', + returned: [], error: null, }, { @@ -58,16 +55,15 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: 'a :: a', - returned: [], userQuery: 'a :: a', + returned: [], error: null, }, { query: 'A::B,C', elems: [ { - name: "a::b", + name: "A::B", fullPath: ["a", "b"], pathWithoutLast: ["a"], pathLast: "b", @@ -75,7 +71,7 @@ const PARSED = [ typeFilter: -1, }, { - name: "c", + name: "C", fullPath: ["c"], pathWithoutLast: [], pathLast: "c", @@ -84,16 +80,15 @@ const PARSED = [ }, ], foundElems: 2, - original: 'A::B,C', + userQuery: 'A::B,C', returned: [], - userQuery: 'a::b,c', error: null, }, { query: 'A::B,C', elems: [ { - name: "a::b", + name: "A::B", fullPath: ["a", "b"], pathWithoutLast: ["a"], pathLast: "b", @@ -109,7 +104,7 @@ const PARSED = [ typeFilter: -1, }, { - name: "c", + name: "C", fullPath: ["c"], pathWithoutLast: [], pathLast: "c", @@ -118,9 +113,8 @@ const PARSED = [ }, ], foundElems: 2, - original: 'A::B,C', + userQuery: 'A::B,C', returned: [], - userQuery: 'a::b,c', error: null, }, { @@ -134,9 +128,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "mod::a", - returned: [], userQuery: "mod::a", + returned: [], error: null, }, ]; diff --git a/tests/rustdoc-js-std/parser-quote.js b/tests/rustdoc-js-std/parser-quote.js index 731673cf4633c..b485047e385eb 100644 --- a/tests/rustdoc-js-std/parser-quote.js +++ b/tests/rustdoc-js-std/parser-quote.js @@ -3,7 +3,7 @@ const PARSED = [ query: '-> "p"', elems: [], foundElems: 1, - original: '-> "p"', + userQuery: '-> "p"', returned: [{ name: "p", fullPath: ["p"], @@ -12,7 +12,6 @@ const PARSED = [ generics: [], typeFilter: -1, }], - userQuery: '-> "p"', error: null, }, { @@ -26,54 +25,48 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: '"p",', - returned: [], userQuery: '"p",', + returned: [], error: null, }, { query: '"p" -> a', elems: [], foundElems: 0, - original: '"p" -> a', - returned: [], userQuery: '"p" -> a', + returned: [], error: "Cannot have more than one element if you use quotes", }, { query: '"a" -> "p"', elems: [], foundElems: 0, - original: '"a" -> "p"', - returned: [], userQuery: '"a" -> "p"', + returned: [], error: "Cannot have more than one literal search element", }, { query: '->"-"', elems: [], foundElems: 0, - original: '->"-"', - returned: [], userQuery: '->"-"', + returned: [], error: 'Unexpected `-` in a string element', }, { query: '"a', elems: [], foundElems: 0, - original: '"a', - returned: [], userQuery: '"a', + returned: [], error: 'Unclosed `"`', }, { query: '""', elems: [], foundElems: 0, - original: '""', - returned: [], userQuery: '""', + returned: [], error: 'Cannot have empty string element', }, ]; diff --git a/tests/rustdoc-js-std/parser-reference.js b/tests/rustdoc-js-std/parser-reference.js index 6b1250146bef8..0fa07ae989528 100644 --- a/tests/rustdoc-js-std/parser-reference.js +++ b/tests/rustdoc-js-std/parser-reference.js @@ -3,18 +3,16 @@ const PARSED = [ query: '&[', elems: [], foundElems: 0, - original: '&[', - returned: [], userQuery: '&[', + returned: [], error: 'Unclosed `[`', }, { query: '[&', elems: [], foundElems: 0, - original: '[&', - returned: [], userQuery: '[&', + returned: [], error: 'Unclosed `[`', }, { @@ -39,7 +37,7 @@ const PARSED = [ pathLast: "reference", generics: [ { - name: "d", + name: "D", fullPath: ["d"], pathWithoutLast: [], pathLast: "d", @@ -65,9 +63,8 @@ const PARSED = [ }, ], foundElems: 2, - original: '&&&D, []', + userQuery: '&&&D, []', returned: [], - userQuery: '&&&d, []', error: null, }, { @@ -98,7 +95,7 @@ const PARSED = [ pathLast: "[]", generics: [ { - name: "d", + name: "D", fullPath: ["d"], pathWithoutLast: [], pathLast: "d", @@ -119,9 +116,8 @@ const PARSED = [ }, ], foundElems: 1, - original: '&&&[D]', + userQuery: '&&&[D]', returned: [], - userQuery: '&&&[d]', error: null, }, { @@ -137,9 +133,8 @@ const PARSED = [ }, ], foundElems: 1, - original: '&', - returned: [], userQuery: '&', + returned: [], error: null, }, { @@ -164,9 +159,8 @@ const PARSED = [ }, ], foundElems: 1, - original: '&mut', - returned: [], userQuery: '&mut', + returned: [], error: null, }, { @@ -190,9 +184,8 @@ const PARSED = [ }, ], foundElems: 2, - original: "&,u8", - returned: [], userQuery: "&,u8", + returned: [], error: null, }, { @@ -225,9 +218,8 @@ const PARSED = [ }, ], foundElems: 2, - original: "&mut,u8", - returned: [], userQuery: "&mut,u8", + returned: [], error: null, }, { @@ -252,9 +244,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "&u8", - returned: [], userQuery: "&u8", + returned: [], error: null, }, { @@ -288,9 +279,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "&u8", - returned: [], userQuery: "&u8", + returned: [], error: null, }, { @@ -324,9 +314,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "u8<&u8>", - returned: [], userQuery: "u8<&u8>", + returned: [], error: null, }, { @@ -368,9 +357,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "u8<&u8, u8>", - returned: [], userQuery: "u8<&u8, u8>", + returned: [], error: null, }, { @@ -404,9 +392,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "u8<&u8>", - returned: [], userQuery: "u8<&u8>", + returned: [], error: null, }, { @@ -456,9 +443,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "u8<&mut u8, u8>", - returned: [], userQuery: "u8<&mut u8, u8>", + returned: [], error: null, }, { @@ -483,18 +469,16 @@ const PARSED = [ }, ], foundElems: 1, - original: "primitive:&u8", - returned: [], userQuery: "primitive:&u8", + returned: [], error: null, }, { query: 'macro:&u8', elems: [], foundElems: 0, - original: "macro:&u8", - returned: [], userQuery: "macro:&u8", + returned: [], error: "Invalid search type: primitive `&` and `macro` both specified", }, { @@ -519,9 +503,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "¯o:u8", - returned: [], userQuery: "¯o:u8", + returned: [], error: null, }, ]; diff --git a/tests/rustdoc-js-std/parser-returned.js b/tests/rustdoc-js-std/parser-returned.js index 8f68209bb966f..30ce26a892059 100644 --- a/tests/rustdoc-js-std/parser-returned.js +++ b/tests/rustdoc-js-std/parser-returned.js @@ -3,15 +3,15 @@ const PARSED = [ query: "-> F

", elems: [], foundElems: 1, - original: "-> F

", + userQuery: "-> F

", returned: [{ - name: "f", + name: "F", fullPath: ["f"], pathWithoutLast: [], pathLast: "f", generics: [ { - name: "p", + name: "P", fullPath: ["p"], pathWithoutLast: [], pathLast: "p", @@ -20,30 +20,28 @@ const PARSED = [ ], typeFilter: -1, }], - userQuery: "-> f

", error: null, }, { query: "-> P", elems: [], foundElems: 1, - original: "-> P", + userQuery: "-> P", returned: [{ - name: "p", + name: "P", fullPath: ["p"], pathWithoutLast: [], pathLast: "p", generics: [], typeFilter: -1, }], - userQuery: "-> p", error: null, }, { query: "->,a", elems: [], foundElems: 1, - original: "->,a", + userQuery: "->,a", returned: [{ name: "a", fullPath: ["a"], @@ -52,7 +50,6 @@ const PARSED = [ generics: [], typeFilter: -1, }], - userQuery: "->,a", error: null, }, { @@ -66,7 +63,7 @@ const PARSED = [ typeFilter: -1, }], foundElems: 2, - original: "aaaaa->a", + userQuery: "aaaaa->a", returned: [{ name: "a", fullPath: ["a"], @@ -75,14 +72,13 @@ const PARSED = [ generics: [], typeFilter: -1, }], - userQuery: "aaaaa->a", error: null, }, { query: "-> !", elems: [], foundElems: 1, - original: "-> !", + userQuery: "-> !", returned: [{ name: "never", fullPath: ["never"], @@ -91,7 +87,6 @@ const PARSED = [ generics: [], typeFilter: 1, }], - userQuery: "-> !", error: null, }, { @@ -105,9 +100,8 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "a->", - returned: [], userQuery: "a->", + returned: [], hasReturnArrow: true, error: null, }, @@ -122,9 +116,8 @@ const PARSED = [ typeFilter: 1, }], foundElems: 1, - original: "!->", - returned: [], userQuery: "!->", + returned: [], hasReturnArrow: true, error: null, }, @@ -139,9 +132,8 @@ const PARSED = [ typeFilter: 1, }], foundElems: 1, - original: "! ->", - returned: [], userQuery: "! ->", + returned: [], hasReturnArrow: true, error: null, }, @@ -156,9 +148,8 @@ const PARSED = [ typeFilter: 1, }], foundElems: 1, - original: "primitive:!->", - returned: [], userQuery: "primitive:!->", + returned: [], hasReturnArrow: true, error: null, }, diff --git a/tests/rustdoc-js-std/parser-separators.js b/tests/rustdoc-js-std/parser-separators.js index 7f95f61b006bb..cf271c80cdce2 100644 --- a/tests/rustdoc-js-std/parser-separators.js +++ b/tests/rustdoc-js-std/parser-separators.js @@ -14,9 +14,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "aaaaaa b", - returned: [], userQuery: "aaaaaa b", + returned: [], error: null, }, { @@ -40,9 +39,8 @@ const PARSED = [ }, ], foundElems: 2, - original: "aaaaaa, b", - returned: [], userQuery: "aaaaaa, b", + returned: [], error: null, }, { @@ -58,9 +56,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "a b", - returned: [], userQuery: "a b", + returned: [], error: null, }, { @@ -84,9 +81,8 @@ const PARSED = [ }, ], foundElems: 2, - original: "a,b", - returned: [], userQuery: "a,b", + returned: [], error: null, }, { @@ -102,9 +98,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "a b", - returned: [], userQuery: "a b", + returned: [], error: null, }, { @@ -128,9 +123,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "a", - returned: [], userQuery: "a", + returned: [], error: null, }, { @@ -161,9 +155,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "a", - returned: [], userQuery: "a", + returned: [], error: null, }, { @@ -187,9 +180,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "a", - returned: [], userQuery: "a", + returned: [], error: null, }, ]; diff --git a/tests/rustdoc-js-std/parser-slice-array.js b/tests/rustdoc-js-std/parser-slice-array.js index 1de52af94e6b0..6579794553599 100644 --- a/tests/rustdoc-js-std/parser-slice-array.js +++ b/tests/rustdoc-js-std/parser-slice-array.js @@ -3,9 +3,8 @@ const PARSED = [ query: '[[[D, []]]', elems: [], foundElems: 0, - original: '[[[D, []]]', + userQuery: '[[[D, []]]', returned: [], - userQuery: '[[[d, []]]', error: 'Unclosed `[`', }, { @@ -30,7 +29,7 @@ const PARSED = [ pathLast: "[]", generics: [ { - name: "d", + name: "D", fullPath: ["d"], pathWithoutLast: [], pathLast: "d", @@ -56,9 +55,8 @@ const PARSED = [ }, ], foundElems: 1, - original: '[[[D, []]]]', + userQuery: '[[[D, []]]]', returned: [], - userQuery: '[[[d, []]]]', error: null, }, { @@ -82,9 +80,8 @@ const PARSED = [ }, ], foundElems: 2, - original: "[],u8", - returned: [], userQuery: "[],u8", + returned: [], error: null, }, { @@ -109,9 +106,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "[u8]", - returned: [], userQuery: "[u8]", + returned: [], error: null, }, { @@ -144,9 +140,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "[u8,u8]", - returned: [], userQuery: "[u8,u8]", + returned: [], error: null, }, { @@ -180,9 +175,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "[u8]", - returned: [], userQuery: "[u8]", + returned: [], error: null, }, { @@ -198,90 +192,80 @@ const PARSED = [ }, ], foundElems: 1, - original: "[]", - returned: [], userQuery: "[]", + returned: [], error: null, }, { query: '[>', elems: [], foundElems: 0, - original: "[>", - returned: [], userQuery: "[>", + returned: [], error: "Unexpected `>` after `[`", }, { query: '[<', elems: [], foundElems: 0, - original: "[<", - returned: [], userQuery: "[<", + returned: [], error: "Found generics without a path", }, { query: '[a>', elems: [], foundElems: 0, - original: "[a>", - returned: [], userQuery: "[a>", + returned: [], error: "Unexpected `>` after `[`", }, { query: '[a<', elems: [], foundElems: 0, - original: "[a<", - returned: [], userQuery: "[a<", + returned: [], error: "Unclosed `<`", }, { query: '[a', elems: [], foundElems: 0, - original: "[a", - returned: [], userQuery: "[a", + returned: [], error: "Unclosed `[`", }, { query: '[', elems: [], foundElems: 0, - original: "[", - returned: [], userQuery: "[", + returned: [], error: "Unclosed `[`", }, { query: ']', elems: [], foundElems: 0, - original: "]", - returned: [], userQuery: "]", + returned: [], error: "Unexpected `]`", }, { query: '[a', elems: [], foundElems: 0, - original: "[a", - returned: [], userQuery: "[a", + returned: [], error: "Unclosed `[`", }, { query: 'a]', elems: [], foundElems: 0, - original: "a]", - returned: [], userQuery: "a]", + returned: [], error: "Unexpected `]` after `>`", }, { @@ -306,18 +290,16 @@ const PARSED = [ }, ], foundElems: 1, - original: "primitive:[u8]", - returned: [], userQuery: "primitive:[u8]", + returned: [], error: null, }, { query: 'macro:[u8]', elems: [], foundElems: 0, - original: "macro:[u8]", - returned: [], userQuery: "macro:[u8]", + returned: [], error: "Invalid search type: primitive `[]` and `macro` both specified", }, ]; diff --git a/tests/rustdoc-js-std/parser-tuple.js b/tests/rustdoc-js-std/parser-tuple.js index eb16289d3c055..6192506838708 100644 --- a/tests/rustdoc-js-std/parser-tuple.js +++ b/tests/rustdoc-js-std/parser-tuple.js @@ -3,9 +3,8 @@ const PARSED = [ query: '(((D, ()))', elems: [], foundElems: 0, - original: '(((D, ()))', + userQuery: '(((D, ()))', returned: [], - userQuery: '(((d, ()))', error: 'Unclosed `(`', }, { @@ -18,7 +17,7 @@ const PARSED = [ pathLast: "()", generics: [ { - name: "d", + name: "D", fullPath: ["d"], pathWithoutLast: [], pathLast: "d", @@ -38,9 +37,8 @@ const PARSED = [ } ], foundElems: 1, - original: '(((D, ())))', + userQuery: '(((D, ())))', returned: [], - userQuery: '(((d, ())))', error: null, }, { @@ -64,9 +62,8 @@ const PARSED = [ }, ], foundElems: 2, - original: "(),u8", - returned: [], userQuery: "(),u8", + returned: [], error: null, }, // Parens act as grouping operators when: @@ -88,9 +85,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "(u8)", - returned: [], userQuery: "(u8)", + returned: [], error: null, }, { @@ -115,9 +111,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "(u8,)", - returned: [], userQuery: "(u8,)", + returned: [], error: null, }, { @@ -142,9 +137,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "(,u8)", - returned: [], userQuery: "(,u8)", + returned: [], error: null, }, { @@ -169,9 +163,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "primitive:(u8)", - returned: [], userQuery: "primitive:(u8)", + returned: [], error: null, }, { @@ -187,9 +180,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "(primitive:u8)", - returned: [], userQuery: "(primitive:u8)", + returned: [], error: null, }, { @@ -222,9 +214,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "(u8,u8)", - returned: [], userQuery: "(u8,u8)", + returned: [], error: null, }, { @@ -249,9 +240,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "(u8)", - returned: [], userQuery: "(u8)", + returned: [], error: null, }, { @@ -267,99 +257,88 @@ const PARSED = [ }, ], foundElems: 1, - original: "()", - returned: [], userQuery: "()", + returned: [], error: null, }, { query: '(>', elems: [], foundElems: 0, - original: "(>", - returned: [], userQuery: "(>", + returned: [], error: "Unexpected `>` after `(`", }, { query: '(<', elems: [], foundElems: 0, - original: "(<", - returned: [], userQuery: "(<", + returned: [], error: "Found generics without a path", }, { query: '(a>', elems: [], foundElems: 0, - original: "(a>", - returned: [], userQuery: "(a>", + returned: [], error: "Unexpected `>` after `(`", }, { query: '(a<', elems: [], foundElems: 0, - original: "(a<", - returned: [], userQuery: "(a<", + returned: [], error: "Unclosed `<`", }, { query: '(a', elems: [], foundElems: 0, - original: "(a", - returned: [], userQuery: "(a", + returned: [], error: "Unclosed `(`", }, { query: '(', elems: [], foundElems: 0, - original: "(", - returned: [], userQuery: "(", + returned: [], error: "Unclosed `(`", }, { query: ')', elems: [], foundElems: 0, - original: ")", - returned: [], userQuery: ")", + returned: [], error: "Unexpected `)`", }, { query: '(a', elems: [], foundElems: 0, - original: "(a", - returned: [], userQuery: "(a", + returned: [], error: "Unclosed `(`", }, { query: 'a)', elems: [], foundElems: 0, - original: "a)", - returned: [], userQuery: "a)", + returned: [], error: "Unexpected `)` after `>`", }, { query: 'macro:(u8)', elems: [], foundElems: 0, - original: "macro:(u8)", - returned: [], userQuery: "macro:(u8)", + returned: [], error: "Invalid search type: primitive `()` and `macro` both specified", }, ]; diff --git a/tests/rustdoc-js-std/parser-weird-queries.js b/tests/rustdoc-js-std/parser-weird-queries.js index 499b82a346948..828b0a7d9f66a 100644 --- a/tests/rustdoc-js-std/parser-weird-queries.js +++ b/tests/rustdoc-js-std/parser-weird-queries.js @@ -15,9 +15,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "a b", - returned: [], userQuery: "a b", + returned: [], error: null, }, { @@ -32,9 +31,8 @@ const PARSED = [ }, ], foundElems: 1, - original: "a b", - returned: [], userQuery: "a b", + returned: [], error: null, }, { @@ -56,36 +54,32 @@ const PARSED = [ }, ], foundElems: 2, - original: "aaa,a", - returned: [], userQuery: "aaa,a", + returned: [], error: null, }, { query: ',,,,', elems: [], foundElems: 0, - original: ",,,,", - returned: [], userQuery: ",,,,", + returned: [], error: null, }, { query: 'mod :', elems: [], foundElems: 0, - original: 'mod :', - returned: [], userQuery: 'mod :', + returned: [], error: "Unexpected `:` (expected path after type filter `mod:`)", }, { query: 'mod\t:', elems: [], foundElems: 0, - original: 'mod :', - returned: [], userQuery: 'mod :', + returned: [], error: "Unexpected `:` (expected path after type filter `mod:`)", }, ]; diff --git a/tests/rustdoc-js/assoc-type-unbound.js b/tests/rustdoc-js/assoc-type-unbound.js new file mode 100644 index 0000000000000..611b8bd1501c7 --- /dev/null +++ b/tests/rustdoc-js/assoc-type-unbound.js @@ -0,0 +1,39 @@ +// exact-check + +const EXPECTED = [ + // Trait-associated types (that is, associated types with no constraints) + // are treated like type parameters, so that you can "pattern match" + // them. We should avoid redundant output (no `Item=MyIter::Item` stuff) + // and should give reasonable results + { + 'query': 'MyIter -> Option', + 'correction': null, + 'others': [ + { + 'path': 'assoc_type_unbound::MyIter', + 'name': 'next', + 'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>', + 'displayMappedNames': 'MyIter::Item = T', + 'displayWhereClause': '', + }, + ], + }, + { + 'query': 'MyIter -> Option', + 'correction': null, + 'others': [ + { + 'path': 'assoc_type_unbound::MyIter', + 'name': 'next', + 'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>', + 'displayMappedNames': 'MyIter::Item = T', + 'displayWhereClause': '', + }, + ], + }, + { + 'query': 'MyIter -> Option', + 'correction': null, + 'others': [], + }, +]; diff --git a/tests/rustdoc-js/assoc-type-unbound.rs b/tests/rustdoc-js/assoc-type-unbound.rs new file mode 100644 index 0000000000000..713b77b500723 --- /dev/null +++ b/tests/rustdoc-js/assoc-type-unbound.rs @@ -0,0 +1,4 @@ +pub trait MyIter { + type Item; + fn next(&mut self) -> Option; +} diff --git a/tests/rustdoc-js/assoc-type.js b/tests/rustdoc-js/assoc-type.js index eec4e7a8258fb..0edf10e794ed7 100644 --- a/tests/rustdoc-js/assoc-type.js +++ b/tests/rustdoc-js/assoc-type.js @@ -7,16 +7,40 @@ const EXPECTED = [ 'query': 'iterator -> u32', 'correction': null, 'others': [ - { 'path': 'assoc_type::my', 'name': 'other_fn' }, - { 'path': 'assoc_type', 'name': 'my_fn' }, + { + 'path': 'assoc_type::my', + 'name': 'other_fn', + 'displayType': 'X -> `u32`', + 'displayMappedNames': '', + 'displayWhereClause': 'X: `Iterator`<`Something`>', + }, + { + 'path': 'assoc_type', + 'name': 'my_fn', + 'displayType': 'X -> `u32`', + 'displayMappedNames': '', + 'displayWhereClause': 'X: `Iterator`', + }, ], }, { 'query': 'iterator', 'correction': null, 'in_args': [ - { 'path': 'assoc_type::my', 'name': 'other_fn' }, - { 'path': 'assoc_type', 'name': 'my_fn' }, + { + 'path': 'assoc_type::my', + 'name': 'other_fn', + 'displayType': 'X -> u32', + 'displayMappedNames': '', + 'displayWhereClause': 'X: `Iterator`<`Something`>', + }, + { + 'path': 'assoc_type', + 'name': 'my_fn', + 'displayType': 'X -> u32', + 'displayMappedNames': '', + 'displayWhereClause': 'X: `Iterator`', + }, ], }, { @@ -26,8 +50,20 @@ const EXPECTED = [ { 'path': 'assoc_type', 'name': 'Something' }, ], 'in_args': [ - { 'path': 'assoc_type::my', 'name': 'other_fn' }, - { 'path': 'assoc_type', 'name': 'my_fn' }, + { + 'path': 'assoc_type::my', + 'name': 'other_fn', + 'displayType': '`X` -> u32', + 'displayMappedNames': '', + 'displayWhereClause': 'X: Iterator<`Something`>', + }, + { + 'path': 'assoc_type', + 'name': 'my_fn', + 'displayType': '`X` -> u32', + 'displayMappedNames': '', + 'displayWhereClause': 'X: Iterator', + }, ], }, // if I write an explicit binding, only it shows up diff --git a/tests/rustdoc-js/generics-trait.js b/tests/rustdoc-js/generics-trait.js index a71393b5e0502..8da9c67050e62 100644 --- a/tests/rustdoc-js/generics-trait.js +++ b/tests/rustdoc-js/generics-trait.js @@ -5,10 +5,22 @@ const EXPECTED = [ 'query': 'Result', 'correction': null, 'in_args': [ - { 'path': 'generics_trait', 'name': 'beta' }, + { + 'path': 'generics_trait', + 'name': 'beta', + 'displayType': '`Result`<`T`, ()> -> ()', + 'displayMappedNames': '', + 'displayWhereClause': 'T: `SomeTrait`', + }, ], 'returned': [ - { 'path': 'generics_trait', 'name': 'bet' }, + { + 'path': 'generics_trait', + 'name': 'bet', + 'displayType': ' -> `Result`<`T`, ()>', + 'displayMappedNames': '', + 'displayWhereClause': 'T: `SomeTrait`', + }, ], }, { @@ -25,20 +37,44 @@ const EXPECTED = [ 'query': 'OtherThingxxxxxxxx', 'correction': null, 'in_args': [ - { 'path': 'generics_trait', 'name': 'alpha' }, + { + 'path': 'generics_trait', + 'name': 'alpha', + 'displayType': 'Result<`T`, ()> -> ()', + 'displayMappedNames': '', + 'displayWhereClause': 'T: `OtherThingxxxxxxxx`', + }, ], 'returned': [ - { 'path': 'generics_trait', 'name': 'alef' }, + { + 'path': 'generics_trait', + 'name': 'alef', + 'displayType': ' -> Result<`T`, ()>', + 'displayMappedNames': '', + 'displayWhereClause': 'T: `OtherThingxxxxxxxx`', + }, ], }, { 'query': 'OtherThingxxxxxxxy', 'correction': 'OtherThingxxxxxxxx', 'in_args': [ - { 'path': 'generics_trait', 'name': 'alpha' }, + { + 'path': 'generics_trait', + 'name': 'alpha', + 'displayType': 'Result<`T`, ()> -> ()', + 'displayMappedNames': '', + 'displayWhereClause': 'T: `OtherThingxxxxxxxx`', + }, ], 'returned': [ - { 'path': 'generics_trait', 'name': 'alef' }, + { + 'path': 'generics_trait', + 'name': 'alef', + 'displayType': ' -> Result<`T`, ()>', + 'displayMappedNames': '', + 'displayWhereClause': 'T: `OtherThingxxxxxxxx`', + }, ], }, ]; diff --git a/tests/rustdoc-js/non-english-identifier.js b/tests/rustdoc-js/non-english-identifier.js index 1765a69152a8f..6a2c4cc637c96 100644 --- a/tests/rustdoc-js/non-english-identifier.js +++ b/tests/rustdoc-js/non-english-identifier.js @@ -11,30 +11,29 @@ const PARSED = [ }], returned: [], foundElems: 1, - original: "中文", userQuery: "中文", error: null, }, { query: '_0Mixed中英文', elems: [{ - name: "_0mixed中英文", + name: "_0Mixed中英文", fullPath: ["_0mixed中英文"], pathWithoutLast: [], pathLast: "_0mixed中英文", + normalizedPathLast: "0mixed中英文", generics: [], typeFilter: -1, }], foundElems: 1, - original: "_0Mixed中英文", + userQuery: "_0Mixed中英文", returned: [], - userQuery: "_0mixed中英文", error: null, }, { query: 'my_crate::中文API', elems: [{ - name: "my_crate::中文api", + name: "my_crate::中文API", fullPath: ["my_crate", "中文api"], pathWithoutLast: ["my_crate"], pathLast: "中文api", @@ -42,26 +41,25 @@ const PARSED = [ typeFilter: -1, }], foundElems: 1, - original: "my_crate::中文API", + userQuery: "my_crate::中文API", returned: [], - userQuery: "my_crate::中文api", error: null, }, { query: '类型A,类型B<约束C>->返回类型<关联类型=路径::约束D>', elems: [{ - name: "类型a", + name: "类型A", fullPath: ["类型a"], pathWithoutLast: [], pathLast: "类型a", generics: [], }, { - name: "类型b", + name: "类型B", fullPath: ["类型b"], pathWithoutLast: [], pathLast: "类型b", generics: [{ - name: "约束c", + name: "约束C", fullPath: ["约束c"], pathWithoutLast: [], pathLast: "约束c", @@ -71,15 +69,21 @@ const PARSED = [ foundElems: 3, totalElems: 5, literalSearch: true, - original: "类型A,类型B<约束C>->返回类型<关联类型=路径::约束D>", + userQuery: "类型A,类型B<约束C>->返回类型<关联类型=路径::约束D>", returned: [{ name: "返回类型", fullPath: ["返回类型"], pathWithoutLast: [], pathLast: "返回类型", generics: [], + bindings: [["关联类型", [{ + name: "路径::约束D", + fullPath: ["路径", "约束d"], + pathWithoutLast: ["路径"], + pathLast: "约束d", + generics: [], + }]]], }], - userQuery: "类型a,类型b<约束c>->返回类型<关联类型=路径::约束d>", error: null, }, { @@ -93,18 +97,16 @@ const PARSED = [ typeFilter: 16, }], foundElems: 1, - original: "my_crate 中文宏!", - returned: [], userQuery: "my_crate 中文宏!", + returned: [], error: null, }, { query: '非法符号——', elems: [], foundElems: 0, - original: "非法符号——", - returned: [], userQuery: "非法符号——", + returned: [], error: "Unexpected `—` after `号` (not a valid identifier)", } ]