From dfd9e5e3fab422684f1768561e19869fbf5f6277 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 10 Mar 2023 18:18:43 -0700 Subject: [PATCH 01/14] rustdoc: use restricted Damerau-Levenshtein distance for search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on https://github.com/rust-lang/rust/pull/108200, for the same rationale. > This replaces the existing Levenshtein algorithm with the > Damerau-Levenshtein algorithm. This means that "ab" to "ba" is one change > (a transposition) instead of two (a deletion and insertion). More > specifically, this is a restricted implementation, in that "ca" to "abc" > cannot be performed as "ca" → "ac" → "abc", as there is an insertion in the > middle of a transposition. I believe that errors like that are sufficiently > rare that it's not worth taking into account. Before this change, searching `prinltn!` listed `print!` first, followed by `println!`. With this change, `println!` matches more closely. --- src/librustdoc/html/static/js/search.js | 362 ++++++++++++++---------- tests/rustdoc-js-std/println-typo.js | 12 + 2 files changed, 227 insertions(+), 147 deletions(-) create mode 100644 tests/rustdoc-js-std/println-typo.js diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index b98bced41261c..d23806f0b70b3 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -76,39 +76,105 @@ function printTab(nb) { } /** - * A function to compute the Levenshtein distance between two strings - * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported - * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode - * This code is an unmodified version of the code written by Marco de Wit - * and was found at https://stackoverflow.com/a/18514751/745719 + * The [edit distance] is a metric for measuring the difference between two strings. + * + * [edit distance]: https://en.wikipedia.org/wiki/Edit_distance */ -const levenshtein_row2 = []; -function levenshtein(s1, s2) { - if (s1 === s2) { - return 0; + +/* + * This function was translated, mostly line-for-line, from + * https://github.com/rust-lang/rust/blob/ff4b772f805ec1e/compiler/rustc_span/src/edit_distance.rs + * + * The current implementation is the restricted Damerau-Levenshtein algorithm. It is restricted + * because it does not permit modifying characters that have already been transposed. The specific + * algorithm should not matter to the caller of the methods, which is why it is not noted in the + * documentation. + */ +let editDistanceCurrent = []; +let editDistancePrev = []; +let editDistancePrevPrev = []; +function editDistance(a, b, limit) { + // Ensure that `b` is the shorter string, minimizing memory use. + if (a.length < b.length) { + const aTmp = a; + a = b; + b = aTmp; + } + + const minDist = a.length - b.length; + // If we know the limit will be exceeded, we can return early. + if (minDist > limit) { + return limit + 1; + } + + // Strip common prefix. + // We know that `b` is the shorter string, so we don't need to check + // `a.length`. + while (b.length > 0 && b[0] === a[0]) { + a = a.substring(1); + b = b.substring(1); + } + // Strip common suffix. + while (b.length > 0 && b[b.length - 1] === a[a.length - 1]) { + a = a.substring(0, a.length - 1); + b = b.substring(0, b.length - 1); } - const s1_len = s1.length, s2_len = s2.length; - if (s1_len && s2_len) { - let i1 = 0, i2 = 0, a, b, c, c2; - const row = levenshtein_row2; - while (i1 < s1_len) { - row[i1] = ++i1; - } - while (i2 < s2_len) { - c2 = s2.charCodeAt(i2); - a = i2; - ++i2; - b = i2; - for (i1 = 0; i1 < s1_len; ++i1) { - c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0); - a = row[i1]; - b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c); - row[i1] = b; - } - } - return b; + + // If either string is empty, the distance is the length of the other. + // We know that `b` is the shorter string, so we don't need to check `a`. + if (b.length === 0) { + return minDist; + } + + const aLength = a.length; + const bLength = b.length; + + for (let i = 0; i <= bLength; ++i) { + editDistanceCurrent[i] = 0; + editDistancePrev[i] = i; + editDistancePrevPrev[i] = Number.MAX_VALUE; } - return s1_len + s2_len; + + // row by row + for (let i = 1; i <= aLength; ++i) { + editDistanceCurrent[0] = i; + const aIdx = i - 1; + + // column by column + for (let j = 1; j <= bLength; ++j) { + const bIdx = j - 1; + + // There is no cost to substitute a character with itself. + const substitutionCost = a[aIdx] === b[bIdx] ? 0 : 1; + + editDistanceCurrent[j] = Math.min( + // deletion + editDistancePrev[j] + 1, + // insertion + editDistanceCurrent[j - 1] + 1, + // substitution + editDistancePrev[j - 1] + substitutionCost + ); + + if ((i > 1) && (j > 1) && (a[aIdx] === b[bIdx - 1]) && (a[aIdx - 1] === b[bIdx])) { + // transposition + editDistanceCurrent[j] = Math.min( + editDistanceCurrent[j], + editDistancePrevPrev[j - 2] + 1 + ); + } + } + + // Rotate the buffers, reusing the memory + const prevPrevTmp = editDistancePrevPrev; + editDistancePrevPrev = editDistancePrev; + editDistancePrev = editDistanceCurrent; + editDistanceCurrent = prevPrevTmp; + } + + // `prev` because we already rotated the buffers. + const distance = editDistancePrev[bLength]; + return distance <= limit ? distance : (limit + 1); } function initSearch(rawSearchIndex) { @@ -802,7 +868,7 @@ function initSearch(rawSearchIndex) { for (const result of results) { if (result.id > -1) { const obj = searchIndex[result.id]; - obj.lev = result.lev; + obj.dist = result.dist; const res = buildHrefAndPath(obj); obj.displayPath = pathSplitter(res[0]); obj.fullPath = obj.displayPath + obj.name; @@ -860,8 +926,8 @@ function initSearch(rawSearchIndex) { // Sort by distance in the path part, if specified // (less changes required to match means higher rankings) - a = aaa.path_lev; - b = bbb.path_lev; + a = aaa.path_dist; + b = bbb.path_dist; if (a !== b) { return a - b; } @@ -875,8 +941,8 @@ function initSearch(rawSearchIndex) { // Sort by distance in the name part, the last part of the path // (less changes required to match means higher rankings) - a = (aaa.lev); - b = (bbb.lev); + a = (aaa.dist); + b = (bbb.dist); if (a !== b) { return a - b; } @@ -961,19 +1027,20 @@ function initSearch(rawSearchIndex) { /** * This function checks if the object (`row`) generics match the given type (`elem`) - * generics. If there are no generics on `row`, `defaultLev` is returned. + * generics. If there are no generics on `row`, `defaultDistance` is returned. * - * @param {Row} row - The object to check. - * @param {QueryElement} elem - The element from the parsed query. - * @param {integer} defaultLev - This is the value to return in case there are no generics. + * @param {Row} row - The object to check. + * @param {QueryElement} elem - The element from the parsed query. + * @param {integer} defaultDistance - This is the value to return in case there are no + * generics. * - * @return {integer} - Returns the best match (if any) or `maxLevDistance + 1`. + * @return {integer} - Returns the best match (if any) or `maxEditDistance + 1`. */ - function checkGenerics(row, elem, defaultLev, maxLevDistance) { + function checkGenerics(row, elem, defaultDistance, maxEditDistance) { if (row.generics.length === 0) { - return elem.generics.length === 0 ? defaultLev : maxLevDistance + 1; + return elem.generics.length === 0 ? defaultDistance : maxEditDistance + 1; } else if (row.generics.length > 0 && row.generics[0].name === null) { - return checkGenerics(row.generics[0], elem, defaultLev, maxLevDistance); + return checkGenerics(row.generics[0], elem, defaultDistance, maxEditDistance); } // The names match, but we need to be sure that all generics kinda // match as well. @@ -984,8 +1051,9 @@ function initSearch(rawSearchIndex) { elem_name = entry.name; if (elem_name === "") { // Pure generic, needs to check into it. - if (checkGenerics(entry, elem, maxLevDistance + 1, maxLevDistance) !== 0) { - return maxLevDistance + 1; + if (checkGenerics(entry, elem, maxEditDistance + 1, maxEditDistance) + !== 0) { + return maxEditDistance + 1; } continue; } @@ -1012,7 +1080,7 @@ function initSearch(rawSearchIndex) { } } if (match === null) { - return maxLevDistance + 1; + return maxEditDistance + 1; } elems[match] -= 1; if (elems[match] === 0) { @@ -1021,7 +1089,7 @@ function initSearch(rawSearchIndex) { } return 0; } - return maxLevDistance + 1; + return maxEditDistance + 1; } /** @@ -1031,17 +1099,17 @@ function initSearch(rawSearchIndex) { * @param {Row} row * @param {QueryElement} elem - The element from the parsed query. * - * @return {integer} - Returns a Levenshtein distance to the best match. + * @return {integer} - Returns an edit distance to the best match. */ - function checkIfInGenerics(row, elem, maxLevDistance) { - let lev = maxLevDistance + 1; + function checkIfInGenerics(row, elem, maxEditDistance) { + let dist = maxEditDistance + 1; for (const entry of row.generics) { - lev = Math.min(checkType(entry, elem, true, maxLevDistance), lev); - if (lev === 0) { + dist = Math.min(checkType(entry, elem, true, maxEditDistance), dist); + if (dist === 0) { break; } } - return lev; + return dist; } /** @@ -1052,21 +1120,21 @@ function initSearch(rawSearchIndex) { * @param {QueryElement} elem - The element from the parsed query. * @param {boolean} literalSearch * - * @return {integer} - Returns a Levenshtein distance to the best match. If there is - * no match, returns `maxLevDistance + 1`. + * @return {integer} - Returns an edit distance to the best match. If there is + * no match, returns `maxEditDistance + 1`. */ - function checkType(row, elem, literalSearch, maxLevDistance) { + function checkType(row, elem, literalSearch, maxEditDistance) { if (row.name === null) { // This is a pure "generic" search, no need to run other checks. if (row.generics.length > 0) { - return checkIfInGenerics(row, elem, maxLevDistance); + return checkIfInGenerics(row, elem, maxEditDistance); } - return maxLevDistance + 1; + return maxEditDistance + 1; } - let lev = levenshtein(row.name, elem.name); + let dist = editDistance(row.name, elem.name, maxEditDistance); if (literalSearch) { - if (lev !== 0) { + if (dist !== 0) { // The name didn't match, let's try to check if the generics do. if (elem.generics.length === 0) { const checkGeneric = row.generics.length > 0; @@ -1075,44 +1143,44 @@ function initSearch(rawSearchIndex) { return 0; } } - return maxLevDistance + 1; + return maxEditDistance + 1; } else if (elem.generics.length > 0) { - return checkGenerics(row, elem, maxLevDistance + 1, maxLevDistance); + return checkGenerics(row, elem, maxEditDistance + 1, maxEditDistance); } return 0; } else if (row.generics.length > 0) { if (elem.generics.length === 0) { - if (lev === 0) { + if (dist === 0) { return 0; } // The name didn't match so we now check if the type we're looking for is inside // the generics! - lev = Math.min(lev, checkIfInGenerics(row, elem, maxLevDistance)); - return lev; - } else if (lev > maxLevDistance) { + dist = Math.min(dist, checkIfInGenerics(row, elem, maxEditDistance)); + return dist; + } else if (dist > maxEditDistance) { // So our item's name doesn't match at all and has generics. // // Maybe it's present in a sub generic? For example "f>>()", if we're // looking for "B", we'll need to go down. - return checkIfInGenerics(row, elem, maxLevDistance); + return checkIfInGenerics(row, elem, maxEditDistance); } else { // At this point, the name kinda match and we have generics to check, so // let's go! - const tmp_lev = checkGenerics(row, elem, lev, maxLevDistance); - if (tmp_lev > maxLevDistance) { - return maxLevDistance + 1; + const tmp_dist = checkGenerics(row, elem, dist, maxEditDistance); + if (tmp_dist > maxEditDistance) { + return maxEditDistance + 1; } // We compute the median value of both checks and return it. - return (tmp_lev + lev) / 2; + return (tmp_dist + dist) / 2; } } else if (elem.generics.length > 0) { // In this case, we were expecting generics but there isn't so we simply reject this // one. - return maxLevDistance + 1; + return maxEditDistance + 1; } // No generics on our query or on the target type so we can return without doing // anything else. - return lev; + return dist; } /** @@ -1122,27 +1190,27 @@ function initSearch(rawSearchIndex) { * @param {QueryElement} elem - The element from the parsed query. * @param {integer} typeFilter * - * @return {integer} - Returns a Levenshtein distance to the best match. If there is no - * match, returns `maxLevDistance + 1`. + * @return {integer} - Returns an edit distance to the best match. If there is no + * match, returns `maxEditDistance + 1`. */ - function findArg(row, elem, typeFilter, maxLevDistance) { - let lev = maxLevDistance + 1; + function findArg(row, elem, typeFilter, maxEditDistance) { + let dist = maxEditDistance + 1; if (row && row.type && row.type.inputs && row.type.inputs.length > 0) { for (const input of row.type.inputs) { if (!typePassesFilter(typeFilter, input.ty)) { continue; } - lev = Math.min( - lev, - checkType(input, elem, parsedQuery.literalSearch, maxLevDistance) + dist = Math.min( + dist, + checkType(input, elem, parsedQuery.literalSearch, maxEditDistance) ); - if (lev === 0) { + if (dist === 0) { return 0; } } } - return parsedQuery.literalSearch ? maxLevDistance + 1 : lev; + return parsedQuery.literalSearch ? maxEditDistance + 1 : dist; } /** @@ -1152,11 +1220,11 @@ function initSearch(rawSearchIndex) { * @param {QueryElement} elem - The element from the parsed query. * @param {integer} typeFilter * - * @return {integer} - Returns a Levenshtein distance to the best match. If there is no - * match, returns `maxLevDistance + 1`. + * @return {integer} - Returns an edit distance to the best match. If there is no + * match, returns `maxEditDistance + 1`. */ - function checkReturned(row, elem, typeFilter, maxLevDistance) { - let lev = maxLevDistance + 1; + function checkReturned(row, elem, typeFilter, maxEditDistance) { + let dist = maxEditDistance + 1; if (row && row.type && row.type.output.length > 0) { const ret = row.type.output; @@ -1164,23 +1232,23 @@ function initSearch(rawSearchIndex) { if (!typePassesFilter(typeFilter, ret_ty.ty)) { continue; } - lev = Math.min( - lev, - checkType(ret_ty, elem, parsedQuery.literalSearch, maxLevDistance) + dist = Math.min( + dist, + checkType(ret_ty, elem, parsedQuery.literalSearch, maxEditDistance) ); - if (lev === 0) { + if (dist === 0) { return 0; } } } - return parsedQuery.literalSearch ? maxLevDistance + 1 : lev; + return parsedQuery.literalSearch ? maxEditDistance + 1 : dist; } - function checkPath(contains, ty, maxLevDistance) { + function checkPath(contains, ty, maxEditDistance) { if (contains.length === 0) { return 0; } - let ret_lev = maxLevDistance + 1; + let ret_dist = maxEditDistance + 1; const path = ty.path.split("::"); if (ty.parent && ty.parent.name) { @@ -1190,27 +1258,27 @@ function initSearch(rawSearchIndex) { const length = path.length; const clength = contains.length; if (clength > length) { - return maxLevDistance + 1; + return maxEditDistance + 1; } for (let i = 0; i < length; ++i) { if (i + clength > length) { break; } - let lev_total = 0; + let dist_total = 0; let aborted = false; for (let x = 0; x < clength; ++x) { - const lev = levenshtein(path[i + x], contains[x]); - if (lev > maxLevDistance) { + const dist = editDistance(path[i + x], contains[x], maxEditDistance); + if (dist > maxEditDistance) { aborted = true; break; } - lev_total += lev; + dist_total += dist; } if (!aborted) { - ret_lev = Math.min(ret_lev, Math.round(lev_total / clength)); + ret_dist = Math.min(ret_dist, Math.round(dist_total / clength)); } } - return ret_lev; + return ret_dist; } function typePassesFilter(filter, type) { @@ -1304,31 +1372,31 @@ function initSearch(rawSearchIndex) { * This function adds the given result into the provided `results` map if it matches the * following condition: * - * * If it is a "literal search" (`parsedQuery.literalSearch`), then `lev` must be 0. - * * If it is not a "literal search", `lev` must be <= `maxLevDistance`. + * * If it is a "literal search" (`parsedQuery.literalSearch`), then `dist` must be 0. + * * If it is not a "literal search", `dist` must be <= `maxEditDistance`. * * The `results` map contains information which will be used to sort the search results: * * * `fullId` is a `string`` used as the key of the object we use for the `results` map. * * `id` is the index in both `searchWords` and `searchIndex` arrays for this element. * * `index` is an `integer`` used to sort by the position of the word in the item's name. - * * `lev` is the main metric used to sort the search results. - * * `path_lev` is zero if a single-component search query is used, otherwise it's the + * * `dist` is the main metric used to sort the search results. + * * `path_dist` is zero if a single-component search query is used, otherwise it's the * distance computed for everything other than the last path component. * * @param {Results} results * @param {string} fullId * @param {integer} id * @param {integer} index - * @param {integer} lev - * @param {integer} path_lev + * @param {integer} dist + * @param {integer} path_dist */ - function addIntoResults(results, fullId, id, index, lev, path_lev, maxLevDistance) { - const inBounds = lev <= maxLevDistance || index !== -1; - if (lev === 0 || (!parsedQuery.literalSearch && inBounds)) { + function addIntoResults(results, fullId, id, index, dist, path_dist, maxEditDistance) { + const inBounds = dist <= maxEditDistance || index !== -1; + if (dist === 0 || (!parsedQuery.literalSearch && inBounds)) { if (results[fullId] !== undefined) { const result = results[fullId]; - if (result.dontValidate || result.lev <= lev) { + if (result.dontValidate || result.dist <= dist) { return; } } @@ -1336,8 +1404,8 @@ function initSearch(rawSearchIndex) { id: id, index: index, dontValidate: parsedQuery.literalSearch, - lev: lev, - path_lev: path_lev, + dist: dist, + path_dist: path_dist, }; } } @@ -1346,7 +1414,7 @@ function initSearch(rawSearchIndex) { * This function is called in case the query is only one element (with or without generics). * This element will be compared to arguments' and returned values' items and also to items. * - * Other important thing to note: since there is only one element, we use levenshtein + * Other important thing to note: since there is only one element, we use edit * distance for name comparisons. * * @param {Row} row @@ -1364,22 +1432,22 @@ function initSearch(rawSearchIndex) { results_others, results_in_args, results_returned, - maxLevDistance + maxEditDistance ) { if (!row || (filterCrates !== null && row.crate !== filterCrates)) { return; } - let lev, index = -1, path_lev = 0; + let dist, index = -1, path_dist = 0; const fullId = row.id; const searchWord = searchWords[pos]; - const in_args = findArg(row, elem, parsedQuery.typeFilter, maxLevDistance); - const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxLevDistance); + const in_args = findArg(row, elem, parsedQuery.typeFilter, maxEditDistance); + const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxEditDistance); - // path_lev is 0 because no parent path information is currently stored + // path_dist is 0 because no parent path information is currently stored // in the search index - addIntoResults(results_in_args, fullId, pos, -1, in_args, 0, maxLevDistance); - addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxLevDistance); + addIntoResults(results_in_args, fullId, pos, -1, in_args, 0, maxEditDistance); + addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxEditDistance); if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) { return; @@ -1403,34 +1471,34 @@ function initSearch(rawSearchIndex) { // No need to check anything else if it's a "pure" generics search. if (elem.name.length === 0) { if (row.type !== null) { - lev = checkGenerics(row.type, elem, maxLevDistance + 1, maxLevDistance); - // path_lev is 0 because we know it's empty - addIntoResults(results_others, fullId, pos, index, lev, 0, maxLevDistance); + dist = checkGenerics(row.type, elem, maxEditDistance + 1, maxEditDistance); + // path_dist is 0 because we know it's empty + addIntoResults(results_others, fullId, pos, index, dist, 0, maxEditDistance); } return; } if (elem.fullPath.length > 1) { - path_lev = checkPath(elem.pathWithoutLast, row, maxLevDistance); - if (path_lev > maxLevDistance) { + path_dist = checkPath(elem.pathWithoutLast, row, maxEditDistance); + if (path_dist > maxEditDistance) { return; } } if (parsedQuery.literalSearch) { if (searchWord === elem.name) { - addIntoResults(results_others, fullId, pos, index, 0, path_lev); + addIntoResults(results_others, fullId, pos, index, 0, path_dist); } return; } - lev = levenshtein(searchWord, elem.pathLast); + dist = editDistance(searchWord, elem.pathLast, maxEditDistance); - if (index === -1 && lev + path_lev > maxLevDistance) { + if (index === -1 && dist + path_dist > maxEditDistance) { return; } - addIntoResults(results_others, fullId, pos, index, lev, path_lev, maxLevDistance); + addIntoResults(results_others, fullId, pos, index, dist, path_dist, maxEditDistance); } /** @@ -1442,22 +1510,22 @@ function initSearch(rawSearchIndex) { * @param {integer} pos - Position in the `searchIndex`. * @param {Object} results */ - function handleArgs(row, pos, results, maxLevDistance) { + function handleArgs(row, pos, results, maxEditDistance) { if (!row || (filterCrates !== null && row.crate !== filterCrates)) { return; } - let totalLev = 0; - let nbLev = 0; + let totalDist = 0; + let nbDist = 0; // If the result is too "bad", we return false and it ends this search. function checkArgs(elems, callback) { for (const elem of elems) { // There is more than one parameter to the query so all checks should be "exact" - const lev = callback(row, elem, NO_TYPE_FILTER, maxLevDistance); - if (lev <= 1) { - nbLev += 1; - totalLev += lev; + const dist = callback(row, elem, NO_TYPE_FILTER, maxEditDistance); + if (dist <= 1) { + nbDist += 1; + totalDist += dist; } else { return false; } @@ -1471,11 +1539,11 @@ function initSearch(rawSearchIndex) { return; } - if (nbLev === 0) { + if (nbDist === 0) { return; } - const lev = Math.round(totalLev / nbLev); - addIntoResults(results, row.id, pos, 0, lev, 0, maxLevDistance); + const dist = Math.round(totalDist / nbDist); + addIntoResults(results, row.id, pos, 0, dist, 0, maxEditDistance); } function innerRunQuery() { @@ -1488,7 +1556,7 @@ function initSearch(rawSearchIndex) { for (const elem of parsedQuery.returned) { queryLen += elem.name.length; } - const maxLevDistance = Math.floor(queryLen / 3); + const maxEditDistance = Math.floor(queryLen / 3); if (parsedQuery.foundElems === 1) { if (parsedQuery.elems.length === 1) { @@ -1503,7 +1571,7 @@ function initSearch(rawSearchIndex) { results_others, results_in_args, results_returned, - maxLevDistance + maxEditDistance ); } } else if (parsedQuery.returned.length === 1) { @@ -1515,14 +1583,14 @@ function initSearch(rawSearchIndex) { row, elem, parsedQuery.typeFilter, - maxLevDistance + maxEditDistance ); - addIntoResults(results_others, row.id, i, -1, in_returned, maxLevDistance); + addIntoResults(results_others, row.id, i, -1, in_returned, maxEditDistance); } } } else if (parsedQuery.foundElems > 0) { for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) { - handleArgs(searchIndex[i], i, results_others, maxLevDistance); + handleArgs(searchIndex[i], i, results_others, maxEditDistance); } } } @@ -1560,7 +1628,7 @@ function initSearch(rawSearchIndex) { * * @return {boolean} - Whether the result is valid or not */ - function validateResult(name, path, keys, parent, maxLevDistance) { + function validateResult(name, path, keys, parent, maxEditDistance) { if (!keys || !keys.length) { return true; } @@ -1574,8 +1642,8 @@ function initSearch(rawSearchIndex) { // next if there is a parent, check for exact parent match (parent !== undefined && parent.name !== undefined && parent.name.toLowerCase().indexOf(key) > -1) || - // lastly check to see if the name was a levenshtein match - levenshtein(name, key) <= maxLevDistance)) { + // lastly check to see if the name was an editDistance match + editDistance(name, key, maxEditDistance) <= maxEditDistance)) { return false; } } diff --git a/tests/rustdoc-js-std/println-typo.js b/tests/rustdoc-js-std/println-typo.js new file mode 100644 index 0000000000000..7ca3ab8e56333 --- /dev/null +++ b/tests/rustdoc-js-std/println-typo.js @@ -0,0 +1,12 @@ +// exact-check + +const QUERY = 'prinltn'; +const FILTER_CRATE = 'std'; + +const EXPECTED = { + 'others': [ + { 'path': 'std', 'name': 'println' }, + { 'path': 'std', 'name': 'print' }, + { 'path': 'std', 'name': 'eprintln' }, + ], +}; From a2341fbbc2f9b35292473f139d17316a55d9e3d0 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 11 Mar 2023 11:19:25 -0800 Subject: [PATCH 02/14] Introduce `Rc::into_inner`, as a parallel to `Arc::into_inner` Unlike `Arc`, `Rc` doesn't have the same race condition to avoid, but maintaining an equivalent API still makes it easier to work with both `Rc` and `Arc`. --- library/alloc/src/rc.rs | 18 ++++++++++++++++++ library/alloc/src/rc/tests.rs | 16 ++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index c9aa23fc4af1f..0e0cf145a992f 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -681,6 +681,24 @@ impl Rc { Err(this) } } + + /// Returns the inner value, if the `Rc` has exactly one strong reference. + /// + /// Otherwise, [`None`] is returned and the `Rc` is dropped. + /// + /// This will succeed even if there are outstanding weak references. + /// + /// If `Rc::into_inner` is called on every clone of this `Rc`, + /// it is guaranteed that exactly one of the calls returns the inner value. + /// This means in particular that the inner value is not dropped. + /// + /// This is equivalent to `Rc::try_unwrap(...).ok()`. (Note that these are not equivalent for + /// `Arc`, due to race conditions that do not apply to `Rc`.) + #[inline] + #[unstable(feature = "rc_into_inner", issue = "106894")] + pub fn into_inner(this: Self) -> Option { + Rc::try_unwrap(this).ok() + } } impl Rc<[T]> { diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs index 32433cfbdcff6..342dc686fa3cb 100644 --- a/library/alloc/src/rc/tests.rs +++ b/library/alloc/src/rc/tests.rs @@ -151,6 +151,22 @@ fn try_unwrap() { assert_eq!(Rc::try_unwrap(x), Ok(5)); } +#[test] +fn into_inner() { + let x = Rc::new(3); + assert_eq!(Rc::into_inner(x), Some(3)); + + let x = Rc::new(4); + let y = Rc::clone(&x); + assert_eq!(Rc::into_inner(x), None); + assert_eq!(Rc::into_inner(y), Some(4)); + + let x = Rc::new(5); + let _w = Rc::downgrade(&x); + assert_eq!(Rc::into_inner(x), Some(5)); +} + + #[test] fn into_from_raw() { let x = Rc::new(Box::new("hello")); From bd4355500a53ba2b3d82d754d1d669710d4b442c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 11 Mar 2023 21:18:45 +0000 Subject: [PATCH 03/14] Gate all usages of dyn*, even in macros --- compiler/rustc_ast_passes/src/feature_gate.rs | 4 +--- compiler/rustc_parse/src/parser/ty.rs | 2 ++ tests/ui/dyn-star/feature-gate-dyn_star.rs | 2 +- tests/ui/dyn-star/feature-gate-dyn_star.stderr | 4 ++-- tests/ui/dyn-star/gated-span.rs | 8 ++++++++ tests/ui/dyn-star/gated-span.stderr | 12 ++++++++++++ tests/ui/dyn-star/no-explicit-dyn-star-cast.rs | 4 ++-- tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr | 8 ++++---- 8 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 tests/ui/dyn-star/gated-span.rs create mode 100644 tests/ui/dyn-star/gated-span.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 96042ea30787e..a33f2bd33f19d 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -337,9 +337,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ast::TyKind::Never => { gate_feature_post!(&self, never_type, ty.span, "the `!` type is experimental"); } - ast::TyKind::TraitObject(_, ast::TraitObjectSyntax::DynStar, ..) => { - gate_feature_post!(&self, dyn_star, ty.span, "dyn* trait objects are unstable"); - } _ => {} } visit::walk_ty(self, ty) @@ -594,6 +591,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(inline_const_pat, "inline-const in pattern position is experimental"); gate_all!(associated_const_equality, "associated const equality is incomplete"); gate_all!(yeet_expr, "`do yeet` expression is experimental"); + gate_all!(dyn_star, "`dyn*` trait objects are experimental"); // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 6fe4da71f6b13..3d9d2cc62e385 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -624,10 +624,12 @@ impl<'a> Parser<'a> { /// /// Note that this does *not* parse bare trait objects. fn parse_dyn_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> { + let lo = self.token.span; self.bump(); // `dyn` // parse dyn* types let syntax = if self.eat(&TokenKind::BinOp(token::Star)) { + self.sess.gated_spans.gate(sym::dyn_star, lo.to(self.prev_token.span)); TraitObjectSyntax::DynStar } else { TraitObjectSyntax::Dyn diff --git a/tests/ui/dyn-star/feature-gate-dyn_star.rs b/tests/ui/dyn-star/feature-gate-dyn_star.rs index 4756661cf41b7..41eed71cdc30a 100644 --- a/tests/ui/dyn-star/feature-gate-dyn_star.rs +++ b/tests/ui/dyn-star/feature-gate-dyn_star.rs @@ -3,7 +3,7 @@ /// dyn* is not necessarily the final surface syntax (if we have one at all), /// but for now we will support it to aid in writing tests independently. pub fn dyn_star_parameter(_: &dyn* Send) { - //~^ dyn* trait objects are unstable + //~^ `dyn*` trait objects are experimental } fn main() {} diff --git a/tests/ui/dyn-star/feature-gate-dyn_star.stderr b/tests/ui/dyn-star/feature-gate-dyn_star.stderr index c3449b6278bac..342e71c3a3a92 100644 --- a/tests/ui/dyn-star/feature-gate-dyn_star.stderr +++ b/tests/ui/dyn-star/feature-gate-dyn_star.stderr @@ -1,8 +1,8 @@ -error[E0658]: dyn* trait objects are unstable +error[E0658]: `dyn*` trait objects are experimental --> $DIR/feature-gate-dyn_star.rs:5:31 | LL | pub fn dyn_star_parameter(_: &dyn* Send) { - | ^^^^^^^^^ + | ^^^^ | = note: see issue #102425 for more information = help: add `#![feature(dyn_star)]` to the crate attributes to enable diff --git a/tests/ui/dyn-star/gated-span.rs b/tests/ui/dyn-star/gated-span.rs new file mode 100644 index 0000000000000..a747987bd2483 --- /dev/null +++ b/tests/ui/dyn-star/gated-span.rs @@ -0,0 +1,8 @@ +macro_rules! t { + ($t:ty) => {} +} + +t!(dyn* Send); +//~^ ERROR `dyn*` trait objects are experimental + +fn main() {} diff --git a/tests/ui/dyn-star/gated-span.stderr b/tests/ui/dyn-star/gated-span.stderr new file mode 100644 index 0000000000000..626b6cd1b7f0e --- /dev/null +++ b/tests/ui/dyn-star/gated-span.stderr @@ -0,0 +1,12 @@ +error[E0658]: `dyn*` trait objects are experimental + --> $DIR/gated-span.rs:5:4 + | +LL | t!(dyn* Send); + | ^^^^ + | + = note: see issue #102425 for more information + = help: add `#![feature(dyn_star)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/dyn-star/no-explicit-dyn-star-cast.rs b/tests/ui/dyn-star/no-explicit-dyn-star-cast.rs index 67240c8e8da06..2d28f516ab5a7 100644 --- a/tests/ui/dyn-star/no-explicit-dyn-star-cast.rs +++ b/tests/ui/dyn-star/no-explicit-dyn-star-cast.rs @@ -4,8 +4,8 @@ fn make_dyn_star() { let i = 42usize; let dyn_i: dyn* Debug = i as dyn* Debug; //~^ ERROR casting `usize` as `dyn* Debug` is invalid - //~| ERROR dyn* trait objects are unstable - //~| ERROR dyn* trait objects are unstable + //~| ERROR `dyn*` trait objects are experimental + //~| ERROR `dyn*` trait objects are experimental } fn main() { diff --git a/tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr b/tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr index eb9c933055abc..78af9c7a3895f 100644 --- a/tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr +++ b/tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr @@ -1,17 +1,17 @@ -error[E0658]: dyn* trait objects are unstable +error[E0658]: `dyn*` trait objects are experimental --> $DIR/no-explicit-dyn-star-cast.rs:5:16 | LL | let dyn_i: dyn* Debug = i as dyn* Debug; - | ^^^^^^^^^^ + | ^^^^ | = note: see issue #102425 for more information = help: add `#![feature(dyn_star)]` to the crate attributes to enable -error[E0658]: dyn* trait objects are unstable +error[E0658]: `dyn*` trait objects are experimental --> $DIR/no-explicit-dyn-star-cast.rs:5:34 | LL | let dyn_i: dyn* Debug = i as dyn* Debug; - | ^^^^^^^^^^ + | ^^^^ | = note: see issue #102425 for more information = help: add `#![feature(dyn_star)]` to the crate attributes to enable From c3159b851a90d3071a97cfb84416f09058c1733a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 11 Mar 2023 21:29:15 +0000 Subject: [PATCH 04/14] Gate const closures even when they appear in macros --- compiler/rustc_ast_passes/src/feature_gate.rs | 9 +-------- compiler/rustc_parse/src/parser/expr.rs | 2 +- compiler/rustc_parse/src/parser/mod.rs | 10 +++++++--- tests/ui/rfc-2632-const-trait-impl/gate.rs | 8 ++++++++ tests/ui/rfc-2632-const-trait-impl/gate.stderr | 15 ++++++++++++--- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index a33f2bd33f19d..9af25e5cae286 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -422,14 +422,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ast::ExprKind::TryBlock(_) => { gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental"); } - ast::ExprKind::Closure(box ast::Closure { constness: ast::Const::Yes(_), .. }) => { - gate_feature_post!( - &self, - const_closures, - e.span, - "const closures are experimental" - ); - } _ => {} } visit::walk_expr(self, e) @@ -592,6 +584,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(associated_const_equality, "associated const equality is incomplete"); gate_all!(yeet_expr, "`do yeet` expression is experimental"); gate_all!(dyn_star, "`dyn*` trait objects are experimental"); + gate_all!(const_closures, "const closures are experimental"); // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e00eda47c663e..1d12dd4709475 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2105,7 +2105,7 @@ impl<'a> Parser<'a> { ClosureBinder::NotPresent }; - let constness = self.parse_closure_constness(Case::Sensitive); + let constness = self.parse_closure_constness(); let movability = if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 6e9b447fa61a9..3251dd6d0c6fb 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1196,9 +1196,13 @@ impl<'a> Parser<'a> { self.parse_constness_(case, false) } - /// Parses constness for closures - fn parse_closure_constness(&mut self, case: Case) -> Const { - self.parse_constness_(case, true) + /// Parses constness for closures (case sensitive, feature-gated) + fn parse_closure_constness(&mut self) -> Const { + let constness = self.parse_constness_(Case::Sensitive, true); + if let Const::Yes(span) = constness { + self.sess.gated_spans.gate(sym::const_closures, span); + } + constness } fn parse_constness_(&mut self, case: Case, is_closure: bool) -> Const { diff --git a/tests/ui/rfc-2632-const-trait-impl/gate.rs b/tests/ui/rfc-2632-const-trait-impl/gate.rs index f2cd26c91b640..d1c93ab9f95af 100644 --- a/tests/ui/rfc-2632-const-trait-impl/gate.rs +++ b/tests/ui/rfc-2632-const-trait-impl/gate.rs @@ -1,5 +1,13 @@ // gate-test-const_closures + fn main() { (const || {})(); //~^ ERROR: const closures are experimental } + +macro_rules! e { + ($e:expr) => {} +} + +e!((const || {})); +//~^ ERROR const closures are experimental diff --git a/tests/ui/rfc-2632-const-trait-impl/gate.stderr b/tests/ui/rfc-2632-const-trait-impl/gate.stderr index 30edc4127e1f4..11cc2cd569a43 100644 --- a/tests/ui/rfc-2632-const-trait-impl/gate.stderr +++ b/tests/ui/rfc-2632-const-trait-impl/gate.stderr @@ -1,12 +1,21 @@ error[E0658]: const closures are experimental - --> $DIR/gate.rs:3:6 + --> $DIR/gate.rs:4:6 | LL | (const || {})(); - | ^^^^^^^^^^^ + | ^^^^^ | = note: see issue #106003 for more information = help: add `#![feature(const_closures)]` to the crate attributes to enable -error: aborting due to previous error +error[E0658]: const closures are experimental + --> $DIR/gate.rs:12:5 + | +LL | e!((const || {})); + | ^^^^^ + | + = note: see issue #106003 for more information + = help: add `#![feature(const_closures)]` to the crate attributes to enable + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. From d2834525ba9378bb69214f3c3f7e868b9f6d4880 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 11 Mar 2023 22:05:50 +0000 Subject: [PATCH 05/14] Use TyCtxt::trait_solver_next in some places --- .../src/traits/query/evaluate_obligation.rs | 15 +++++++-------- .../src/traits/select/mod.rs | 13 ++++++------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index 1420c25c92280..f84b2f4428d1a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -1,6 +1,5 @@ use rustc_middle::traits::solve::{Certainty, Goal, MaybeCause}; use rustc_middle::ty; -use rustc_session::config::TraitSolver; use crate::infer::canonical::OriginalQueryValues; use crate::infer::InferCtxt; @@ -80,13 +79,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { _ => obligation.param_env.without_const(), }; - if self.tcx.sess.opts.unstable_opts.trait_solver != TraitSolver::Next { - let c_pred = self.canonicalize_query_keep_static( - param_env.and(obligation.predicate), - &mut _orig_values, - ); - self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred) - } else { + if self.tcx.trait_solver_next() { self.probe(|snapshot| { if let Ok((_, certainty)) = self.evaluate_root_goal(Goal::new(self.tcx, param_env, obligation.predicate)) @@ -111,6 +104,12 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { Ok(EvaluationResult::EvaluatedToErr) } }) + } else { + let c_pred = self.canonicalize_query_keep_static( + param_env.and(obligation.predicate), + &mut _orig_values, + ); + self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred) } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index ba4df2b3720ef..d7ce007812450 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -50,7 +50,6 @@ use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::SubstsRef; use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; -use rustc_session::config::TraitSolver; use rustc_span::symbol::sym; use std::cell::{Cell, RefCell}; @@ -545,13 +544,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PredicateObligation<'tcx>, ) -> Result { self.evaluation_probe(|this| { - if this.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Next { + if this.tcx().trait_solver_next() { + this.evaluate_predicates_recursively_in_new_solver([obligation.clone()]) + } else { this.evaluate_predicate_recursively( TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), obligation.clone(), ) - } else { - this.evaluate_predicates_recursively_in_new_solver([obligation.clone()]) } }) } @@ -591,7 +590,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { where I: IntoIterator> + std::fmt::Debug, { - if self.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Next { + if self.tcx().trait_solver_next() { + self.evaluate_predicates_recursively_in_new_solver(predicates) + } else { let mut result = EvaluatedToOk; for obligation in predicates { let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?; @@ -604,8 +605,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } Ok(result) - } else { - self.evaluate_predicates_recursively_in_new_solver(predicates) } } From fcb2a3665f691f567dbb359899687d6344c72944 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Sat, 11 Mar 2023 14:10:00 -0800 Subject: [PATCH 06/14] Rename `config.toml.example` to `config.example.toml` --- .gitattributes | 1 - .reuse/dep5 | 2 +- README.md | 2 +- config.toml.example => config.example.toml | 0 src/bootstrap/README.md | 2 +- src/bootstrap/bin/main.rs | 8 ++++---- src/bootstrap/config.rs | 4 ++-- src/bootstrap/configure.py | 6 +++--- src/bootstrap/dist.rs | 2 +- src/bootstrap/native.rs | 2 +- src/doc/rustc/src/instrument-coverage.md | 2 +- triagebot.toml | 2 +- 12 files changed, 16 insertions(+), 17 deletions(-) rename config.toml.example => config.example.toml (100%) diff --git a/.gitattributes b/.gitattributes index 51a670b5fbefd..d29c15fe712f3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,7 +9,6 @@ src/etc/installer/gfx/* binary src/vendor/** -text Cargo.lock linguist-generated=false -config.toml.example linguist-language=TOML # Older git versions try to fix line endings on images and fonts, this prevents it. *.png binary diff --git a/.reuse/dep5 b/.reuse/dep5 index dc6a60e623952..9a59f455fe9af 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -16,7 +16,7 @@ Files: compiler/* Cargo.lock Cargo.toml CODE_OF_CONDUCT.md - config.toml.example + config.example.toml configure CONTRIBUTING.md COPYRIGHT diff --git a/README.md b/README.md index c424bd12ffdbc..c19e129a9207a 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ See [the rustc-dev-guide for more info][sysllvm]. The Rust build system uses a file named `config.toml` in the root of the source tree to determine various configuration settings for the build. Set up the defaults intended for distros to get started. You can see a full - list of options in `config.toml.example`. + list of options in `config.example.toml`. ```sh printf 'profile = "user" \nchangelog-seen = 2 \n' > config.toml diff --git a/config.toml.example b/config.example.toml similarity index 100% rename from config.toml.example rename to config.example.toml diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 71eee8968e272..253d504d7bdb5 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -185,7 +185,7 @@ Some general areas that you may be interested in modifying are: If you make a major change, please remember to: + Update `VERSION` in `src/bootstrap/main.rs`. -* Update `changelog-seen = N` in `config.toml.example`. +* Update `changelog-seen = N` in `config.example.toml`. * Add an entry in `src/bootstrap/CHANGELOG.md`. A 'major change' includes diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index 3856bb64fb310..b345bf9fb8360 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -44,8 +44,8 @@ fn main() { if suggest_setup { println!("warning: you have not made a `config.toml`"); println!( - "help: consider running `./x.py setup` or copying `config.toml.example` by running \ - `cp config.toml.example config.toml`" + "help: consider running `./x.py setup` or copying `config.example.toml` by running \ + `cp config.example.toml config.toml`" ); } else if let Some(suggestion) = &changelog_suggestion { println!("{}", suggestion); @@ -57,8 +57,8 @@ fn main() { if suggest_setup { println!("warning: you have not made a `config.toml`"); println!( - "help: consider running `./x.py setup` or copying `config.toml.example` by running \ - `cp config.toml.example config.toml`" + "help: consider running `./x.py setup` or copying `config.example.toml` by running \ + `cp config.example.toml config.toml`" ); } else if let Some(suggestion) = &changelog_suggestion { println!("{}", suggestion); diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 4f417d36511ba..fc5aa8a245d2d 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -55,7 +55,7 @@ pub enum DryRun { /// Note that this structure is not decoded directly into, but rather it is /// filled out from the decoded forms of the structs below. For documentation /// each field, see the corresponding fields in -/// `config.toml.example`. +/// `config.example.toml`. #[derive(Default)] #[cfg_attr(test, derive(Clone))] pub struct Config { @@ -325,7 +325,7 @@ impl std::str::FromStr for SplitDebuginfo { impl SplitDebuginfo { /// Returns the default `-Csplit-debuginfo` value for the current target. See the comment for - /// `rust.split-debuginfo` in `config.toml.example`. + /// `rust.split-debuginfo` in `config.example.toml`. fn default_for_platform(target: &str) -> Self { if target.contains("apple") { SplitDebuginfo::Unpacked diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 5278f0c10b3d2..b326ae402aa6b 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -194,7 +194,7 @@ def err(msg): print('') print('This configure script is a thin configuration shim over the true') print('configuration system, `config.toml`. You can explore the comments') - print('in `config.toml.example` next to this configure script to see') + print('in `config.example.toml` next to this configure script to see') print('more information about what each option is. Additionally you can') print('pass `--set` as an argument to set arbitrary key/value pairs') print('in the TOML configuration if desired') @@ -367,7 +367,7 @@ def set(key, value): set('build.configure-args', sys.argv[1:]) -# "Parse" the `config.toml.example` file into the various sections, and we'll +# "Parse" the `config.example.toml` file into the various sections, and we'll # use this as a template of a `config.toml` to write out which preserves # all the various comments and whatnot. # @@ -380,7 +380,7 @@ def set(key, value): targets = {} top_level_keys = [] -for line in open(rust_dir + '/config.toml.example').read().split("\n"): +for line in open(rust_dir + '/config.example.toml').read().split("\n"): if cur_section == None: if line.count('=') == 1: top_level_key = line.split('=')[0] diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index c9384004100b2..a3d9cb3e10c21 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -967,7 +967,7 @@ impl Step for PlainSourceTarball { "RELEASES.md", "configure", "x.py", - "config.toml.example", + "config.example.toml", "Cargo.toml", "Cargo.lock", ]; diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 7f6d80c91ade5..040e36ea5f8d8 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -286,7 +286,7 @@ impl Step for Llvm { (true, true) => "RelWithDebInfo", }; - // NOTE: remember to also update `config.toml.example` when changing the + // NOTE: remember to also update `config.example.toml` when changing the // defaults! let llvm_targets = match &builder.config.llvm_targets { Some(s) => s, diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index da91e25595cc7..b0b2f4196422b 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -31,7 +31,7 @@ Rust's source-based code coverage requires the Rust "profiler runtime". Without The Rust `nightly` distribution channel includes the profiler runtime, by default. -> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `config.toml.example`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.]`): +> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `config.example.toml`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.]`): > > ```toml > # Build the profiler runtime (required when compiling with options that depend diff --git a/triagebot.toml b/triagebot.toml index 0388ce9fc9852..a81f66fe16856 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -185,7 +185,7 @@ trigger_files = [ "src/tools/x", "configure", "Cargo.toml", - "config.toml.example", + "config.example.toml", "src/stage0.json" ] From ce795d9ca88bc676aa22efcf2292c475cd1ae39e Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 11 Mar 2023 20:36:43 -0700 Subject: [PATCH 07/14] rustdoc: collapse edit distance state into an object --- src/librustdoc/html/static/js/search.js | 166 ++++++++++++------------ 1 file changed, 86 insertions(+), 80 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index d23806f0b70b3..00d3ef9374d48 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -90,91 +90,97 @@ function printTab(nb) { * algorithm should not matter to the caller of the methods, which is why it is not noted in the * documentation. */ -let editDistanceCurrent = []; -let editDistancePrev = []; -let editDistancePrevPrev = []; -function editDistance(a, b, limit) { - // Ensure that `b` is the shorter string, minimizing memory use. - if (a.length < b.length) { - const aTmp = a; - a = b; - b = aTmp; - } - - const minDist = a.length - b.length; - // If we know the limit will be exceeded, we can return early. - if (minDist > limit) { - return limit + 1; - } - - // Strip common prefix. - // We know that `b` is the shorter string, so we don't need to check - // `a.length`. - while (b.length > 0 && b[0] === a[0]) { - a = a.substring(1); - b = b.substring(1); - } - // Strip common suffix. - while (b.length > 0 && b[b.length - 1] === a[a.length - 1]) { - a = a.substring(0, a.length - 1); - b = b.substring(0, b.length - 1); - } - - // If either string is empty, the distance is the length of the other. - // We know that `b` is the shorter string, so we don't need to check `a`. - if (b.length === 0) { - return minDist; - } - - const aLength = a.length; - const bLength = b.length; - - for (let i = 0; i <= bLength; ++i) { - editDistanceCurrent[i] = 0; - editDistancePrev[i] = i; - editDistancePrevPrev[i] = Number.MAX_VALUE; - } - - // row by row - for (let i = 1; i <= aLength; ++i) { - editDistanceCurrent[0] = i; - const aIdx = i - 1; - - // column by column - for (let j = 1; j <= bLength; ++j) { - const bIdx = j - 1; - - // There is no cost to substitute a character with itself. - const substitutionCost = a[aIdx] === b[bIdx] ? 0 : 1; - - editDistanceCurrent[j] = Math.min( - // deletion - editDistancePrev[j] + 1, - // insertion - editDistanceCurrent[j - 1] + 1, - // substitution - editDistancePrev[j - 1] + substitutionCost - ); - - if ((i > 1) && (j > 1) && (a[aIdx] === b[bIdx - 1]) && (a[aIdx - 1] === b[bIdx])) { - // transposition - editDistanceCurrent[j] = Math.min( - editDistanceCurrent[j], - editDistancePrevPrev[j - 2] + 1 +const editDistanceState = { + current: [], + prev: [], + prevPrev: [], + calculate: function calculate(a, b, limit) { + // Ensure that `b` is the shorter string, minimizing memory use. + if (a.length < b.length) { + const aTmp = a; + a = b; + b = aTmp; + } + + const minDist = a.length - b.length; + // If we know the limit will be exceeded, we can return early. + if (minDist > limit) { + return limit + 1; + } + + // Strip common prefix. + // We know that `b` is the shorter string, so we don't need to check + // `a.length`. + while (b.length > 0 && b[0] === a[0]) { + a = a.substring(1); + b = b.substring(1); + } + // Strip common suffix. + while (b.length > 0 && b[b.length - 1] === a[a.length - 1]) { + a = a.substring(0, a.length - 1); + b = b.substring(0, b.length - 1); + } + + // If either string is empty, the distance is the length of the other. + // We know that `b` is the shorter string, so we don't need to check `a`. + if (b.length === 0) { + return minDist; + } + + const aLength = a.length; + const bLength = b.length; + + for (let i = 0; i <= bLength; ++i) { + this.current[i] = 0; + this.prev[i] = i; + this.prevPrev[i] = Number.MAX_VALUE; + } + + // row by row + for (let i = 1; i <= aLength; ++i) { + this.current[0] = i; + const aIdx = i - 1; + + // column by column + for (let j = 1; j <= bLength; ++j) { + const bIdx = j - 1; + + // There is no cost to substitute a character with itself. + const substitutionCost = a[aIdx] === b[bIdx] ? 0 : 1; + + this.current[j] = Math.min( + // deletion + this.prev[j] + 1, + // insertion + this.current[j - 1] + 1, + // substitution + this.prev[j - 1] + substitutionCost ); + + if ((i > 1) && (j > 1) && (a[aIdx] === b[bIdx - 1]) && (a[aIdx - 1] === b[bIdx])) { + // transposition + this.current[j] = Math.min( + this.current[j], + this.prevPrev[j - 2] + 1 + ); + } } + + // Rotate the buffers, reusing the memory + const prevPrevTmp = this.prevPrev; + this.prevPrev = this.prev; + this.prev = this.current; + this.current = prevPrevTmp; } - // Rotate the buffers, reusing the memory - const prevPrevTmp = editDistancePrevPrev; - editDistancePrevPrev = editDistancePrev; - editDistancePrev = editDistanceCurrent; - editDistanceCurrent = prevPrevTmp; - } + // `prev` because we already rotated the buffers. + const distance = this.prev[bLength]; + return distance <= limit ? distance : (limit + 1); + }, +}; - // `prev` because we already rotated the buffers. - const distance = editDistancePrev[bLength]; - return distance <= limit ? distance : (limit + 1); +function editDistance(a, b, limit) { + return editDistanceState.calculate(a, b, limit); } function initSearch(rawSearchIndex) { From 421c34b2b064d17e79154bf55a95a08764d5b27e Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sun, 12 Mar 2023 11:56:56 +0200 Subject: [PATCH 08/14] typo --- compiler/rustc_type_ir/src/sty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index ebe2b76aef335..62e699eefd706 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -167,7 +167,7 @@ pub enum TyKind { /// lifetimes bound by the witness itself. /// /// This variant is only using when `drop_tracking_mir` is set. - /// This contains the `DefId` and the `SubstRef` of the generator. + /// This contains the `DefId` and the `SubstsRef` of the generator. /// The actual witness types are computed on MIR by the `mir_generator_witnesses` query. /// /// Looking at the following example, the witness for this generator From db266939822a5a89473871abff6beea39b083af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lanteri=20Thauvin?= Date: Thu, 2 Mar 2023 12:37:32 +0100 Subject: [PATCH 09/14] Forbid the use of `#[target_feature]` on `main` --- compiler/rustc_hir_analysis/messages.ftl | 2 ++ compiler/rustc_hir_analysis/src/errors.rs | 8 ++++++++ compiler/rustc_hir_analysis/src/lib.rs | 5 +++++ tests/ui/asm/x86_64/issue-89875.rs | 4 +++- .../issue-108645-target-feature-on-main.rs | 7 +++++++ .../issue-108645-target-feature-on-main.stderr | 8 ++++++++ 6 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs create mode 100644 tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index dbe15ed5e9b99..5800f77c55a9e 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -128,6 +128,8 @@ hir_analysis_where_clause_on_main = `main` function is not allowed to have a `wh hir_analysis_track_caller_on_main = `main` function is not allowed to be `#[track_caller]` .suggestion = remove this annotation +hir_analysis_target_feature_on_main = `main` function is not allowed to have `#[target_feature]` + hir_analysis_start_not_track_caller = `start` is not allowed to be `#[track_caller]` .label = `start` is not allowed to be `#[track_caller]` diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 8a697b4c51488..5b445ead1d3f4 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -327,6 +327,14 @@ pub(crate) struct TrackCallerOnMain { pub annotated: Span, } +#[derive(Diagnostic)] +#[diag(hir_analysis_target_feature_on_main)] +pub(crate) struct TargetFeatureOnMain { + #[primary_span] + #[label(hir_analysis_target_feature_on_main)] + pub main: Span, +} + #[derive(Diagnostic)] #[diag(hir_analysis_start_not_track_caller)] pub(crate) struct StartTrackCaller { diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 6a4f538287897..098b003eaed7d 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -283,6 +283,11 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { error = true; } + if !tcx.codegen_fn_attrs(main_def_id).target_features.is_empty() { + tcx.sess.emit_err(errors::TargetFeatureOnMain { main: main_span }); + error = true; + } + if error { return; } diff --git a/tests/ui/asm/x86_64/issue-89875.rs b/tests/ui/asm/x86_64/issue-89875.rs index 669fd7e7e46e5..39c6456402223 100644 --- a/tests/ui/asm/x86_64/issue-89875.rs +++ b/tests/ui/asm/x86_64/issue-89875.rs @@ -7,7 +7,7 @@ use std::arch::asm; #[target_feature(enable = "avx")] -fn main() { +fn foo() { unsafe { asm!( "/* {} */", @@ -15,3 +15,5 @@ fn main() { ); } } + +fn main() {} diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs new file mode 100644 index 0000000000000..0d59e50264e1b --- /dev/null +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs @@ -0,0 +1,7 @@ +// only-x86_64 + +#![feature(target_feature_11)] + +#[target_feature(enable = "avx2")] +fn main() {} +//~^ ERROR `main` function is not allowed to have `#[target_feature]` diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr new file mode 100644 index 0000000000000..cfafbd52286a7 --- /dev/null +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr @@ -0,0 +1,8 @@ +error: `main` function is not allowed to have `#[target_feature]` + --> $DIR/issue-108645-target-feature-on-main.rs:6:1 + | +LL | fn main() {} + | ^^^^^^^^^ `main` function is not allowed to have `#[target_feature]` + +error: aborting due to previous error + From 963305bda8723a461afe37ddb7fc6da9f56bf100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lanteri=20Thauvin?= Date: Thu, 2 Mar 2023 14:26:12 +0100 Subject: [PATCH 10/14] Forbid the use of `#[target_feature]` on `start` --- compiler/rustc_hir_analysis/messages.ftl | 3 +++ compiler/rustc_hir_analysis/src/errors.rs | 9 +++++++++ compiler/rustc_hir_analysis/src/lib.rs | 7 +++++++ .../issue-108645-target-feature-on-start.rs | 9 +++++++++ .../issue-108645-target-feature-on-start.stderr | 11 +++++++++++ 5 files changed, 39 insertions(+) create mode 100644 tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs create mode 100644 tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 5800f77c55a9e..0105cbf36dee2 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -133,6 +133,9 @@ hir_analysis_target_feature_on_main = `main` function is not allowed to have `#[ hir_analysis_start_not_track_caller = `start` is not allowed to be `#[track_caller]` .label = `start` is not allowed to be `#[track_caller]` +hir_analysis_start_not_target_feature = `start` is not allowed to have `#[target_feature]` + .label = `start` is not allowed to have `#[target_feature]` + hir_analysis_start_not_async = `start` is not allowed to be `async` .label = `start` is not allowed to be `async` diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 5b445ead1d3f4..f57197edeb74d 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -344,6 +344,15 @@ pub(crate) struct StartTrackCaller { pub start: Span, } +#[derive(Diagnostic)] +#[diag(hir_analysis_start_not_target_feature)] +pub(crate) struct StartTargetFeature { + #[primary_span] + pub span: Span, + #[label] + pub start: Span, +} + #[derive(Diagnostic)] #[diag(hir_analysis_start_not_async, code = "E0752")] pub(crate) struct StartAsync { diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 098b003eaed7d..be07bb4584605 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -378,6 +378,13 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { }); error = true; } + if attr.has_name(sym::target_feature) { + tcx.sess.emit_err(errors::StartTargetFeature { + span: attr.span, + start: start_span, + }); + error = true; + } } if error { diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs new file mode 100644 index 0000000000000..50e8ce2fdd5ed --- /dev/null +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs @@ -0,0 +1,9 @@ +// only-x86_64 + +#![feature(start)] +#![feature(target_feature_11)] + +#[start] +#[target_feature(enable = "avx2")] +//~^ ERROR `start` is not allowed to have `#[target_feature]` +fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr new file mode 100644 index 0000000000000..07687f3c7f4a2 --- /dev/null +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr @@ -0,0 +1,11 @@ +error: `start` is not allowed to have `#[target_feature]` + --> $DIR/issue-108645-target-feature-on-start.rs:7:1 + | +LL | #[target_feature(enable = "avx2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } + | -------------------------------------------------------- `start` is not allowed to have `#[target_feature]` + +error: aborting due to previous error + From 29b1789a75c988cedb75dbdd8ebd0397a922e174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lanteri=20Thauvin?= Date: Fri, 10 Mar 2023 13:27:13 +0100 Subject: [PATCH 11/14] Allow `#[target_feature]` on `main` and `start` for WASM --- compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 3 +++ compiler/rustc_hir_analysis/src/lib.rs | 13 +++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index c62968e535420..8b6bf886b0d0b 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -242,6 +242,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { // Note that this is also allowed if `actually_rustdoc` so // if a target is documenting some wasm-specific code then // it's not spuriously denied. + // + // This exception needs to be kept in sync with allowing + // `#[target_feature]` on `main` and `start`. } else if !tcx.features().target_feature_11 { let mut err = feature_err( &tcx.sess.parse_sess, diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index be07bb4584605..62abcbbdc9f6e 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -283,7 +283,11 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { error = true; } - if !tcx.codegen_fn_attrs(main_def_id).target_features.is_empty() { + if !tcx.codegen_fn_attrs(main_def_id).target_features.is_empty() + // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988 + && !tcx.sess.target.is_like_wasm + && !tcx.sess.opts.actually_rustdoc + { tcx.sess.emit_err(errors::TargetFeatureOnMain { main: main_span }); error = true; } @@ -378,7 +382,12 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { }); error = true; } - if attr.has_name(sym::target_feature) { + if attr.has_name(sym::target_feature) + // Calling functions with `#[target_feature]` is + // not unsafe on WASM, see #84988 + && !tcx.sess.target.is_like_wasm + && !tcx.sess.opts.actually_rustdoc + { tcx.sess.emit_err(errors::StartTargetFeature { span: attr.span, start: start_span, From 77a80708523679d749ac066e3a58b6ad86d30102 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 12 Mar 2023 15:06:04 +0100 Subject: [PATCH 12/14] Add eslint check for rustdoc-gui tester --- src/tools/rustdoc-gui/.eslintrc.js | 96 ++++++++++++++++++++++++++++++ src/tools/rustdoc-gui/tester.js | 16 ++--- 2 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 src/tools/rustdoc-gui/.eslintrc.js diff --git a/src/tools/rustdoc-gui/.eslintrc.js b/src/tools/rustdoc-gui/.eslintrc.js new file mode 100644 index 0000000000000..f4aadc071998b --- /dev/null +++ b/src/tools/rustdoc-gui/.eslintrc.js @@ -0,0 +1,96 @@ +module.exports = { + "env": { + "browser": true, + "node": true, + "es6": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module" + }, + "rules": { + "linebreak-style": [ + "error", + "unix" + ], + "semi": [ + "error", + "always" + ], + "quotes": [ + "error", + "double" + ], + "linebreak-style": [ + "error", + "unix" + ], + "no-trailing-spaces": "error", + "no-var": ["error"], + "prefer-const": ["error"], + "prefer-arrow-callback": ["error"], + "brace-style": [ + "error", + "1tbs", + { "allowSingleLine": false } + ], + "keyword-spacing": [ + "error", + { "before": true, "after": true } + ], + "arrow-spacing": [ + "error", + { "before": true, "after": true } + ], + "key-spacing": [ + "error", + { "beforeColon": false, "afterColon": true, "mode": "strict" } + ], + "func-call-spacing": ["error", "never"], + "space-infix-ops": "error", + "space-before-function-paren": ["error", "never"], + "space-before-blocks": "error", + "comma-dangle": ["error", "always-multiline"], + "comma-style": ["error", "last"], + "max-len": ["error", { "code": 100, "tabWidth": 4 }], + "eol-last": ["error", "always"], + "arrow-parens": ["error", "as-needed"], + "no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_" + } + ], + "eqeqeq": "error", + "no-const-assign": "error", + "no-debugger": "error", + "no-dupe-args": "error", + "no-dupe-else-if": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-ex-assign": "error", + "no-fallthrough": "error", + "no-invalid-regexp": "error", + "no-import-assign": "error", + "no-self-compare": "error", + "no-template-curly-in-string": "error", + "block-scoped-var": "error", + "guard-for-in": "error", + "no-alert": "error", + "no-confusing-arrow": "error", + "no-div-regex": "error", + "no-floating-decimal": "error", + "no-implicit-globals": "error", + "no-implied-eval": "error", + "no-label-var": "error", + "no-lonely-if": "error", + "no-mixed-operators": "error", + "no-multi-assign": "error", + "no-return-assign": "error", + "no-script-url": "error", + "no-sequences": "error", + "no-div-regex": "error", + } +}; diff --git a/src/tools/rustdoc-gui/tester.js b/src/tools/rustdoc-gui/tester.js index 80bfd38d1e961..72baad606f01e 100644 --- a/src/tools/rustdoc-gui/tester.js +++ b/src/tools/rustdoc-gui/tester.js @@ -6,8 +6,8 @@ const fs = require("fs"); const path = require("path"); -const os = require('os'); -const {Options, runTest} = require('browser-ui-test'); +const os = require("os"); +const {Options, runTest} = require("browser-ui-test"); // If a test fails or errors, we will retry it two more times in case it was a flaky failure. const NB_RETRY = 3; @@ -200,7 +200,7 @@ async function main(argv) { const framework_options = new Options(); try { // This is more convenient that setting fields one by one. - let args = [ + const args = [ "--variable", "DOC_PATH", opts["doc_folder"], "--enable-fail-on-js-error", "--allow-file-access-from-files", ]; @@ -234,7 +234,7 @@ async function main(argv) { } else { files = opts["files"]; } - files = files.filter(file => path.extname(file) == ".goml"); + files = files.filter(file => path.extname(file) === ".goml"); if (files.length === 0) { console.error("rustdoc-gui: No test selected"); process.exit(2); @@ -259,7 +259,7 @@ async function main(argv) { // We catch this "event" to display a nicer message in case of unexpected exit (because of a // missing `--no-sandbox`). - const exitHandling = (code) => { + const exitHandling = () => { if (!opts["no_sandbox"]) { console.log(""); console.log( @@ -268,10 +268,10 @@ async function main(argv) { console.log(""); } }; - process.on('exit', exitHandling); + process.on("exit", exitHandling); const originalFilesLen = files.length; - let results = createEmptyResults(); + const results = createEmptyResults(); const status_bar = char_printer(files.length); let new_results; @@ -281,7 +281,7 @@ async function main(argv) { Array.prototype.push.apply(results.successful, new_results.successful); // We generate the new list of files with the previously failing tests. files = Array.prototype.concat(new_results.failed, new_results.errored).map( - f => f['file_name']); + f => f["file_name"]); if (files.length > originalFilesLen / 2) { // If we have too many failing tests, it's very likely not flaky failures anymore so // no need to retry. From cd9759ab9269509699a5b1aa6296a3c5e74b8ee4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 12 Mar 2023 15:07:35 +0100 Subject: [PATCH 13/14] Add rustdoc-gui eslint check into CI --- src/ci/docker/host-x86_64/mingw-check/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index 9141d3e8a48c2..515890aef8df8 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -53,4 +53,5 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ # Runs checks to ensure that there are no ES5 issues in our JS code. es-check es6 ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \ - eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js + eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \ + eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js From 992957efa9a4f12641ffb229e3b57c7088d140eb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 12 Mar 2023 11:21:40 -0700 Subject: [PATCH 14/14] Fix formatting of new Rc::into_inner test --- library/alloc/src/rc/tests.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs index 342dc686fa3cb..2784108e0e635 100644 --- a/library/alloc/src/rc/tests.rs +++ b/library/alloc/src/rc/tests.rs @@ -166,7 +166,6 @@ fn into_inner() { assert_eq!(Rc::into_inner(x), Some(5)); } - #[test] fn into_from_raw() { let x = Rc::new(Box::new("hello"));