From 9625ed8be7fa66c3ee5f78180a3d5911817096f6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 19 Jan 2022 22:36:15 +0100 Subject: [PATCH 01/14] Move settings into full JS --- src/librustdoc/html/markdown.rs | 5 +- src/librustdoc/html/render/context.rs | 23 +-- src/librustdoc/html/render/mod.rs | 128 ------------ src/librustdoc/html/static/js/main.js | 179 +++++++++++++---- src/librustdoc/html/static/js/search.js | 16 +- src/librustdoc/html/static/js/settings.js | 183 +++++++++++++++++- .../html/static/js/source-script.js | 4 +- src/librustdoc/html/templates/page.html | 1 - 8 files changed, 343 insertions(+), 196 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 9e76af9829854..56a085c298250 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -1440,6 +1440,10 @@ fn init_id_map() -> FxHashMap, usize> { let mut map = FxHashMap::default(); // This is the list of IDs used in Javascript. map.insert("help".into(), 1); + map.insert("settings".into(), 1); + map.insert("not-displayed".into(), 1); + map.insert("alternative-display".into(), 1); + map.insert("search".into(), 1); // This is the list of IDs used in HTML generated in Rust (including the ones // used in tera template files). map.insert("mainThemeStyle".into(), 1); @@ -1449,7 +1453,6 @@ fn init_id_map() -> FxHashMap, usize> { map.insert("settings-menu".into(), 1); map.insert("help-button".into(), 1); map.insert("main-content".into(), 1); - map.insert("search".into(), 1); map.insert("crate-search".into(), 1); map.insert("render-detail".into(), 1); map.insert("toggle-all-docs".into(), 1); diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 8e643107353dd..a30c533aa48c8 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -17,8 +17,8 @@ use super::print_item::{full_path, item_path, print_item}; use super::search_index::build_index; use super::write_shared::write_shared; use super::{ - collect_spans_and_sources, print_sidebar, scrape_examples_help, settings, AllTypes, - LinkFromSrc, NameDoc, StylePath, BASIC_KEYWORDS, + collect_spans_and_sources, print_sidebar, scrape_examples_help, AllTypes, LinkFromSrc, NameDoc, + StylePath, BASIC_KEYWORDS, }; use crate::clean::{self, types::ExternalLocation, ExternalCrate}; @@ -589,21 +589,18 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { page.root_path = "./"; let sidebar = "

Settings

"; - let theme_names: Vec = self - .shared - .style_files - .iter() - .map(StylePath::basename) - .collect::>()?; let v = layout::render( &self.shared.layout, &page, sidebar, - settings( - self.shared.static_root_path.as_deref().unwrap_or("./"), - &self.shared.resource_suffix, - theme_names, - )?, + |buf: &mut Buffer| { + write!( + buf, + "", + page.static_root_path.unwrap_or(""), + page.resource_suffix + ) + }, &self.shared.style_files, ); self.shared.fs.write(settings_file, v)?; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 7a4289b8e60e9..fedeb449b2e0e 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -334,134 +334,6 @@ impl AllTypes { } } -#[derive(Debug)] -enum Setting { - Section { - description: &'static str, - sub_settings: Vec, - }, - Toggle { - js_data_name: &'static str, - description: &'static str, - default_value: bool, - }, - Select { - js_data_name: &'static str, - description: &'static str, - default_value: &'static str, - options: Vec, - }, -} - -impl Setting { - fn display(&self, root_path: &str, suffix: &str) -> String { - match *self { - Setting::Section { description, ref sub_settings } => format!( - "
\ -
{}
\ -
{}
-
", - description, - sub_settings.iter().map(|s| s.display(root_path, suffix)).collect::() - ), - Setting::Toggle { js_data_name, description, default_value } => format!( - "
\ - \ -
{}
\ -
", - js_data_name, - if default_value { " checked" } else { "" }, - description, - ), - Setting::Select { js_data_name, description, default_value, ref options } => format!( - "
{}
{}
", - js_data_name, - description, - options - .iter() - .map(|opt| format!( - "", - js_data_name = js_data_name, - name = opt, - checked = if opt == default_value { "checked" } else { "" }, - )) - .collect::(), - ), - } - } -} - -impl From<(&'static str, &'static str, bool)> for Setting { - fn from(values: (&'static str, &'static str, bool)) -> Setting { - Setting::Toggle { js_data_name: values.0, description: values.1, default_value: values.2 } - } -} - -impl> From<(&'static str, Vec)> for Setting { - fn from(values: (&'static str, Vec)) -> Setting { - Setting::Section { - description: values.0, - sub_settings: values.1.into_iter().map(|v| v.into()).collect::>(), - } - } -} - -fn settings(root_path: &str, suffix: &str, theme_names: Vec) -> Result { - // (id, explanation, default value) - let settings: &[Setting] = &[ - Setting::from(("use-system-theme", "Use system theme", true)), - Setting::Select { - js_data_name: "theme", - description: "Theme", - default_value: "light", - options: theme_names.clone(), - }, - Setting::Select { - js_data_name: "preferred-light-theme", - description: "Preferred light theme", - default_value: "light", - options: theme_names.clone(), - }, - Setting::Select { - js_data_name: "preferred-dark-theme", - description: "Preferred dark theme", - default_value: "dark", - options: theme_names, - }, - ("auto-hide-large-items", "Auto-hide item contents for large items.", true).into(), - ("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(), - ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", false) - .into(), - ("go-to-only-result", "Directly go to item in search if there is only one result", false) - .into(), - ("line-numbers", "Show line numbers on code examples", false).into(), - ("disable-shortcuts", "Disable keyboard shortcuts", false).into(), - ]; - - Ok(format!( - "
-

\ - Rustdoc settings\ -

\ - \ - Back\ - \ -
\ -
{}
\ - \ - ", - settings.iter().map(|s| s.display(root_path, suffix)).collect::(), - root_path = root_path, - suffix = suffix - )) -} - fn scrape_examples_help(shared: &SharedContext<'_>) -> String { let mut content = SCRAPE_EXAMPLES_HELP_MD.to_owned(); content.push_str(&format!( diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 9e5de9a843ab1..7ac835b327034 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -57,11 +57,20 @@ function resourcePath(basename, extension) { return getVar("root-path") + basename + getVar("resource-suffix") + extension; } +function hideMain() { + addClass(document.getElementById(MAIN_ID), "hidden"); +} + +function showMain() { + removeClass(document.getElementById(MAIN_ID), "hidden"); +} + (function () { window.rootPath = getVar("root-path"); window.currentCrate = getVar("current-crate"); window.searchJS = resourcePath("search", ".js"); window.searchIndexJS = resourcePath("search-index", ".js"); + window.settingsJS = resourcePath("settings", ".js"); const sidebarVars = document.getElementById("sidebar-vars"); if (sidebarVars) { window.sidebarCurrent = { @@ -104,6 +113,9 @@ function getVirtualKey(ev) { const THEME_PICKER_ELEMENT_ID = "theme-picker"; const THEMES_ELEMENT_ID = "theme-choices"; const MAIN_ID = "main-content"; +const SETTINGS_BUTTON_ID = "settings-menu"; +const ALTERNATIVE_DISPLAY_ID = "alternative-display"; +const NOT_DISPLAYED_ID = "not-displayed"; function getThemesElement() { return document.getElementById(THEMES_ELEMENT_ID); @@ -113,6 +125,10 @@ function getThemePickerElement() { return document.getElementById(THEME_PICKER_ELEMENT_ID); } +function getSettingsButton() { + return document.getElementById(SETTINGS_BUTTON_ID); +} + // Returns the current URL without any query parameter or hash. function getNakedUrl() { return window.location.href.split("?")[0].split("#")[0]; @@ -136,6 +152,10 @@ function hideThemeButtonState() { themePicker.style.borderBottomLeftRadius = "3px"; } +window.hideSettings = function() { + // Does nothing by default. +}; + // Set up the theme picker list. (function () { if (!document.location.href.startsWith("file:///")) { @@ -182,14 +202,120 @@ function hideThemeButtonState() { }); }()); +/** + * This function inserts `newNode` after `referenceNode`. It doesn't work if `referenceNode` + * doesn't have a parent node. + * + * @param {DOM} newNode + * @param {DOM} referenceNode + */ +function insertAfter(newNode, referenceNode) { + referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); +} + +/** + * This function creates a new `
` with the given `id` and `classes` if it doesn't already + * exist. + * + * More information about this in `switchDisplayedElement` documentation. + * + * @param {string} id + * @param {string} classes + */ +function getOrCreateSection(id, classes) { + let el = document.getElementById(id); + + if (!el) { + el = document.createElement("section"); + el.id = id; + el.className = classes; + insertAfter(el, document.getElementById(MAIN_ID)); + } + return el; +} + +/** + * Returns the `
` element which contains the displayed element. + * + * @return {DOM} + */ +function getAlternativeDisplayElem() { + return getOrCreateSection(ALTERNATIVE_DISPLAY_ID, "content hidden"); +} + +/** + * Returns the `
` element which contains the not-displayed elements. + * + * @return {DOM} + */ +function getNotDisplayedElem() { + return getOrCreateSection(NOT_DISPLAYED_ID, "hidden"); +} + +/** + * To nicely switch between displayed "extra" elements (such as search results or settings menu) + * and to alternate between the displayed and not displayed elements, we hold them in two different + * `
` elements. They work in pair: one hold the not displayed elements while the other + * contains the displayed element (there can be only one at the same time!). So basically, we switch + * elements between the two `
` elements. + * + * @param {DOM} elemToDisplay + */ +function switchDisplayedElement(elemToDisplay) { + const el = getAlternativeDisplayElem(); + + if (el.children.length > 0) { + getNotDisplayedElem().appendChild(el.firstElementChild); + } + if (elemToDisplay === null) { + addClass(el, "hidden"); + showMain(); + return; + } + el.appendChild(elemToDisplay); + hideMain(); + removeClass(el, "hidden"); +} + +function browserSupportsHistoryApi() { + return window.history && typeof window.history.pushState === "function"; +} + +// eslint-disable-next-line no-unused-vars +function loadCss(cssFileName) { + const link = document.createElement("link"); + link.href = resourcePath(cssFileName, ".css"); + link.type = "text/css"; + link.rel = "stylesheet"; + document.getElementsByTagName("head")[0].appendChild(link); +} + (function() { "use strict"; + function loadScript(url) { + const script = document.createElement('script'); + script.src = url; + document.head.append(script); + } + + + getSettingsButton().onclick = function(event) { + event.preventDefault(); + loadScript(window.settingsJS); + }; + window.searchState = { loadingText: "Loading search results...", input: document.getElementsByClassName("search-input")[0], outputElement: function() { - return document.getElementById("search"); + let el = document.getElementById("search"); + if (!el) { + el = document.createElement("section"); + el.id = "search"; + getNotDisplayedElem().appendChild(el); + } + return el; }, title: document.title, titleBeforeSearch: document.title, @@ -208,6 +334,9 @@ function hideThemeButtonState() { searchState.timeout = null; } }, + isDisplayed: function() { + return searchState.outputElement().parentElement.id === ALTERNATIVE_DISPLAY_ID; + }, // Sets the focus on the search bar at the top of the page focus: function() { searchState.input.focus(); @@ -220,20 +349,15 @@ function hideThemeButtonState() { if (search === null || typeof search === 'undefined') { search = searchState.outputElement(); } - addClass(main, "hidden"); - removeClass(search, "hidden"); + switchDisplayedElement(search); searchState.mouseMovedAfterSearch = false; document.title = searchState.title; }, - hideResults: function(search) { - if (search === null || typeof search === 'undefined') { - search = searchState.outputElement(); - } - addClass(search, "hidden"); - removeClass(main, "hidden"); + hideResults: function() { + switchDisplayedElement(null); document.title = searchState.titleBeforeSearch; // We also remove the query parameter from the URL. - if (searchState.browserSupportsHistoryApi()) { + if (browserSupportsHistoryApi()) { history.replaceState(null, window.currentCrate + " - Rust", getNakedUrl() + window.location.hash); } @@ -248,20 +372,11 @@ function hideThemeButtonState() { }); return params; }, - browserSupportsHistoryApi: function() { - return window.history && typeof window.history.pushState === "function"; - }, setup: function() { const search_input = searchState.input; if (!searchState.input) { return; } - function loadScript(url) { - const script = document.createElement('script'); - script.src = url; - document.head.append(script); - } - let searchLoaded = false; function loadSearch() { if (!searchLoaded) { @@ -303,23 +418,20 @@ function hideThemeButtonState() { } const toggleAllDocsId = "toggle-all-docs"; - const main = document.getElementById(MAIN_ID); let savedHash = ""; function handleHashes(ev) { - let elem; - const search = searchState.outputElement(); - if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) { + if (ev !== null && searchState.isDisplayed() && ev.newURL) { // This block occurs when clicking on an element in the navbar while // in a search. - searchState.hideResults(search); + switchDisplayedElement(null); const hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1); - if (searchState.browserSupportsHistoryApi()) { + if (browserSupportsHistoryApi()) { // `window.location.search`` contains all the query parameters, not just `search`. history.replaceState(null, "", getNakedUrl() + window.location.search + "#" + hash); } - elem = document.getElementById(hash); + const elem = document.getElementById(hash); if (elem) { elem.scrollIntoView(); } @@ -389,14 +501,17 @@ function hideThemeButtonState() { } function handleEscape(ev) { + searchState.clearInputTimeout(); const help = getHelpElement(false); - const search = searchState.outputElement(); if (help && !hasClass(help, "hidden")) { displayHelp(false, ev, help); - } else if (search && !hasClass(search, "hidden")) { - searchState.clearInputTimeout(); + } else { + switchDisplayedElement(null); + if (browserSupportsHistoryApi()) { + history.replaceState(null, window.currentCrate + " - Rust", + getNakedUrl() + window.location.hash); + } ev.preventDefault(); - searchState.hideResults(search); } searchState.defocus(); hideThemeButtonState(); @@ -733,10 +848,6 @@ function hideThemeButtonState() { innerToggle.children[0].innerText = labelForToggleButton(sectionIsCollapsed); } - function insertAfter(newNode, referenceNode) { - referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); - } - (function() { const toggles = document.getElementById(toggleAllDocsId); if (toggles) { diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index a6f7dd74af6b0..84600fa3e094f 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2,7 +2,7 @@ /* eslint no-var: "error" */ /* eslint prefer-const: "error" */ /* global addClass, getNakedUrl, getSettingValue, hasOwnPropertyRustdoc, initSearch, onEach */ -/* global onEachLazy, removeClass, searchState, hasClass */ +/* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi */ (function() { // This mapping table should match the discriminants of @@ -1786,8 +1786,9 @@ window.initSearch = function(rawSearchIndex) { // Because searching is incremental by character, only the most // recent search query is added to the browser history. - if (searchState.browserSupportsHistoryApi()) { + if (browserSupportsHistoryApi()) { const newURL = buildUrl(query.original, filterCrates); + if (!history.state && !params.search) { history.pushState(null, "", newURL); } else { @@ -1965,10 +1966,9 @@ window.initSearch = function(rawSearchIndex) { if (!searchState.input) { return; } - const search = searchState.outputElement(); - if (search_input.value !== "" && hasClass(search, "hidden")) { - searchState.showResults(search); - if (searchState.browserSupportsHistoryApi()) { + if (search_input.value !== "" && !searchState.isDisplayed()) { + searchState.showResults(); + if (browserSupportsHistoryApi()) { history.replaceState(null, "", buildUrl(search_input.value, getFilterCrates())); } @@ -1980,7 +1980,7 @@ window.initSearch = function(rawSearchIndex) { const searchAfter500ms = function() { searchState.clearInputTimeout(); if (searchState.input.value.length === 0) { - if (searchState.browserSupportsHistoryApi()) { + if (browserSupportsHistoryApi()) { history.replaceState(null, window.currentCrate + " - Rust", getNakedUrl() + window.location.hash); } @@ -2058,7 +2058,7 @@ window.initSearch = function(rawSearchIndex) { // Push and pop states are used to add search results to the browser // history. - if (searchState.browserSupportsHistoryApi()) { + if (browserSupportsHistoryApi()) { // Store the previous so we can revert back to it later. const previousTitle = document.title; diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 7bc6f6cfe043d..bdc1162a7c071 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -2,10 +2,13 @@ /* eslint no-var: "error" */ /* eslint prefer-const: "error" */ // Local js definitions: -/* global getSettingValue, getVirtualKey, onEachLazy, updateLocalStorage, updateSystemTheme */ -/* global addClass, removeClass */ +/* global getSettingValue, getVirtualKey, updateLocalStorage, updateSystemTheme, loadCss */ +/* global addClass, removeClass, onEach, onEachLazy, NOT_DISPLAYED_ID */ +/* global MAIN_ID, getVar, getSettingsButton, switchDisplayedElement, getNotDisplayedElem */ (function () { + const isSettingsPage = window.location.pathname.endsWith("/settings.html"); + function changeSetting(settingName, value) { updateLocalStorage(settingName, value); @@ -55,9 +58,9 @@ } } - function setEvents() { + function setEvents(settingsElement) { updateLightAndDark(); - onEachLazy(document.getElementsByClassName("slider"), function(elem) { + onEachLazy(settingsElement.getElementsByClassName("slider"), function(elem) { const toggle = elem.previousElementSibling; const settingId = toggle.id; const settingValue = getSettingValue(settingId); @@ -70,7 +73,7 @@ toggle.onkeyup = handleKey; toggle.onkeyrelease = handleKey; }); - onEachLazy(document.getElementsByClassName("select-wrapper"), function(elem) { + onEachLazy(settingsElement.getElementsByClassName("select-wrapper"), function(elem) { const select = elem.getElementsByTagName("select")[0]; const settingId = select.id; const settingValue = getSettingValue(settingId); @@ -81,7 +84,7 @@ changeSetting(this.id, this.value); }; }); - onEachLazy(document.querySelectorAll("input[type=\"radio\"]"), function(elem) { + onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), function(elem) { const settingId = elem.name; const settingValue = getSettingValue(settingId); if (settingValue !== null && settingValue !== "null") { @@ -91,10 +94,172 @@ changeSetting(ev.target.name, ev.target.value); }); }); - document.getElementById("back").addEventListener("click", function() { - history.back(); + } + + function buildSettings(settings) { + let output = ""; + + onEach(settings, function(setting) { + output += `<div class="setting-line">`; + const js_data_name = setting["js_name"]; + const setting_name = setting["name"]; + + if (setting["options"] !== undefined) { + // This is a select setting. + output += `<div class="radio-line" id="${js_data_name}">\ + <span class="setting-name">${setting_name}</span>\ + <div class="choices">`; + onEach(setting["options"], function(option) { + const checked = option === setting["default"] ? " checked" : ""; + + output += `<label for="${js_data_name}-${option}" class="choice">\ + <input type="radio" name="${js_data_name}" \ + id="${js_data_name}-${option}" value="${option}"${checked}>\ + ${option}\ + </label>`; + }); + output += "</div></div>"; + } else { + // This is a toggle. + const checked = setting["default"] === true ? " checked" : ""; + output += ` + <label class="toggle"> + <input type="checkbox" id="${js_data_name}"${checked}> + <span class="slider"></span> + </label> + <div>${setting_name}</div>`; + } + output += "</div>"; }); + return output; } - window.addEventListener("DOMContentLoaded", setEvents); + function buildSettingsPage(settings) { + // First, we add the settings.css file. + loadCss("settings"); + + // Then we build the DOM. + const el = document.createElement("section"); + el.id = "settings"; + let innerHTML = ` + <div class="main-heading"> + <h1 class="fqn"> + <span class="in-band">Rustdoc settings</span> + </h1> + <span class="out-of-band">`; + + if (isSettingsPage) { + innerHTML += + `<a id="back" href="javascript:void(0)" onclick="history.back();">Back</a>`; + } else { + innerHTML += + `<a id="back" href="javascript:void(0)" onclick="switchDisplayedElement(null);">\ + Back</a>`; + } + innerHTML += `</span> + </div> + <div class="settings">${buildSettings(settings)}</div>`; + + el.innerHTML = innerHTML; + + if (isSettingsPage) { + document.getElementById(MAIN_ID).appendChild(el); + } else { + getNotDisplayedElem().appendChild(el); + } + return el; + } + + function createSettingsPage() { + const themes = getVar("themes").split(","); + const settings = [ + { + "name": "Use system theme", + "js_name": "use-system-theme", + "default": true, + }, + { + "name": "Theme", + "js_name": "theme", + "default": "light", + "options": themes, + }, + { + "name": "Preferred dark theme", + "js_name": "preferred-dark-theme", + "default": "dark", + "options": themes, + }, + { + "name": "Preferred light theme", + "js_name": "preferred-light-theme", + "default": "light", + "options": themes, + }, + { + "name": "Auto-hide item contents for large items", + "js_name": "auto-hide-large-items", + "default": true, + }, + { + "name": "Auto-hide item methods' documentation", + "js_name": "auto-hide-method-docs", + "default": false, + }, + { + "name": "Auto-hide trait implementation documentation", + "js_name": "auto-hide-trait-implementations", + "default": false, + }, + { + "name": "Directly go to item in search if there is only one result", + "js_name": "go-to-only-result", + "default": false, + }, + { + "name": "Show line numbers on code examples", + "js_name": "line-numbers", + "default": false, + }, + { + "name": "Disable keyboard shortcuts", + "js_name": "disable-shortcuts", + "default": false, + }, + ]; + + return buildSettingsPage(settings); + } + + const settingsMenu = createSettingsPage(); + + if (isSettingsPage) { + // We replace the existing "onclick" callback to do nothing if clicked. + getSettingsButton().onclick = function(event) { + event.preventDefault(); + }; + } else { + // We replace the existing "onclick" callback. + const settingsButton = getSettingsButton(); + settingsButton.onclick = function(event) { + event.preventDefault(); + if (settingsMenu.parentElement.id === NOT_DISPLAYED_ID) { + switchDisplayedElement(settingsMenu); + } else { + window.hideSettings(); + } + }; + window.hideSettings = function() { + switchDisplayedElement(null); + }; + } + + // We now wait a bit for the web browser to end re-computing the DOM... + setTimeout(function() { + setEvents(settingsMenu); + // The setting menu is already displayed if we're on the settings page. + if (!isSettingsPage) { + switchDisplayedElement(settingsMenu); + } + }, 10); })(); diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js index c48a847665ef5..6aee0da69f8de 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/source-script.js @@ -6,7 +6,7 @@ /* global search, sourcesIndex */ // Local js definitions: -/* global addClass, getCurrentValue, hasClass, onEachLazy, removeClass, searchState */ +/* global addClass, getCurrentValue, hasClass, onEachLazy, removeClass, browserSupportsHistoryApi */ /* global updateLocalStorage */ (function() { @@ -195,7 +195,7 @@ const handleSourceHighlight = (function() { const set_fragment = function(name) { const x = window.scrollX, y = window.scrollY; - if (searchState.browserSupportsHistoryApi()) { + if (browserSupportsHistoryApi()) { history.replaceState(null, null, "#" + name); highlightSourceLines(); } else { diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index 564731ab7354b..470cce93a5020 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -135,7 +135,6 @@ <h2 class="location"></h2> </nav> {#- -#} </div> {#- -#} <section id="main-content" class="content">{{- content|safe -}}</section> {#- -#} - <section id="search" class="content hidden"></section> {#- -#} </div> {#- -#} </main> {#- -#} {{- layout.external_html.after_content|safe -}} From e7d7d75c81bbe81ab6b536a823cdc6ea9cfcf0ee Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume.gomez@huawei.com> Date: Wed, 19 Jan 2022 23:06:17 +0100 Subject: [PATCH 02/14] Add GUI test for settings menu --- src/test/rustdoc-gui/escape-key.goml | 14 +++++--- src/test/rustdoc-gui/settings.goml | 38 ++++++++++++++++++++++ src/test/rustdoc-gui/theme-change.goml | 1 + src/test/rustdoc-gui/theme-in-history.goml | 12 ++++--- 4 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 src/test/rustdoc-gui/settings.goml diff --git a/src/test/rustdoc-gui/escape-key.goml b/src/test/rustdoc-gui/escape-key.goml index 6e305e81eeec0..8713bf65c8432 100644 --- a/src/test/rustdoc-gui/escape-key.goml +++ b/src/test/rustdoc-gui/escape-key.goml @@ -4,17 +4,20 @@ goto: file://|DOC_PATH|/test_docs/index.html // First, we check that the search results are hidden when the Escape key is pressed. write: (".search-input", "test") wait-for: "#search h1" // The search element is empty before the first search -assert-attribute: ("#search", {"class": "content"}) +// Check that the currently displayed element is search. +wait-for: "#alternative-display #search" assert-attribute: ("#main-content", {"class": "content hidden"}) assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH) press-key: "Escape" -assert-attribute: ("#search", {"class": "content hidden"}) +// Checks that search is no longer in the displayed content. +wait-for: "#not-displayed #search" +assert-false: "#alternative-display #search" assert-attribute: ("#main-content", {"class": "content"}) assert-document-property: ({"URL": "index.html"}, [ENDS_WITH]) // Check that focusing the search input brings back the search results focus: ".search-input" -assert-attribute: ("#search", {"class": "content"}) +wait-for: "#alternative-display #search" assert-attribute: ("#main-content", {"class": "content hidden"}) assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH) @@ -24,8 +27,8 @@ click: "#help-button" assert-document-property: ({"URL": "index.html?search=test"}, [ENDS_WITH]) assert-attribute: ("#help", {"class": ""}) press-key: "Escape" +wait-for: "#alternative-display #search" assert-attribute: ("#help", {"class": "hidden"}) -assert-attribute: ("#search", {"class": "content"}) assert-attribute: ("#main-content", {"class": "content hidden"}) assert-document-property: ({"URL": "index.html?search=test"}, [ENDS_WITH]) @@ -37,5 +40,6 @@ assert-false: ".search-input:focus" assert: "#results a:focus" press-key: "Escape" assert-attribute: ("#help", {"class": "hidden"}) -assert-attribute: ("#search", {"class": "content hidden"}) +wait-for: "#not-displayed #search" +assert-false: "#alternative-display #search" assert-attribute: ("#main-content", {"class": "content"}) diff --git a/src/test/rustdoc-gui/settings.goml b/src/test/rustdoc-gui/settings.goml new file mode 100644 index 0000000000000..45a7f8bcdde2a --- /dev/null +++ b/src/test/rustdoc-gui/settings.goml @@ -0,0 +1,38 @@ +// This test ensures that the settings menu display is working as expected. +goto: file://|DOC_PATH|/test_docs/index.html +// First, we check that the settings page doesn't exist. +assert-false: "#settings" +// We now click on the settings button. +click: "#settings-menu" +wait-for: "#settings" +assert: "#main-content.hidden" +assert-css: ("#settings", {"display": "block"}) +// Let's close it by clicking on the same button. +click: "#settings-menu" +assert-false: "#alternative-display #settings" +assert: "#not-displayed #settings" +assert: "#main-content:not(.hidden)" + +// Let's open and then close it again with the "close settings" button. +click: "#settings-menu" +wait-for: "#alternative-display #settings" +assert: "#main-content.hidden" +click: "#back" +wait-for: "#not-displayed #settings" +assert: "#main-content:not(.hidden)" + +// Let's check that pressing "ESCAPE" is closing it. +click: "#settings-menu" +wait-for: "#alternative-display #settings" +press-key: "Escape" +wait-for: "#not-displayed #settings" +assert: "#main-content:not(.hidden)" + +// Let's click on it when the search results are displayed. +focus: ".search-input" +write: "test" +wait-for: "#alternative-display #search" +click: "#settings-menu" +wait-for: "#alternative-display #settings" +assert: "#not-displayed #search" +assert: "#main-content.hidden" diff --git a/src/test/rustdoc-gui/theme-change.goml b/src/test/rustdoc-gui/theme-change.goml index 333391ba27970..9706511ea19c3 100644 --- a/src/test/rustdoc-gui/theme-change.goml +++ b/src/test/rustdoc-gui/theme-change.goml @@ -9,6 +9,7 @@ click: "#theme-choices > button:last-child" wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" }) goto: file://|DOC_PATH|/settings.html +wait-for: "#settings" click: "#theme-light" wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" }) assert-local-storage: { "rustdoc-theme": "light" } diff --git a/src/test/rustdoc-gui/theme-in-history.goml b/src/test/rustdoc-gui/theme-in-history.goml index 3b66c85d8dad2..f576ced1c6208 100644 --- a/src/test/rustdoc-gui/theme-in-history.goml +++ b/src/test/rustdoc-gui/theme-in-history.goml @@ -1,15 +1,19 @@ // Ensures that the theme is working when going back in history. goto: file://|DOC_PATH|/test_docs/index.html // Set the theme to dark. -local-storage: {"rustdoc-theme": "dark", "rustdoc-preferred-dark-theme": "dark", "rustdoc-use-system-theme": "false"} +local-storage: { + "rustdoc-theme": "dark", + "rustdoc-preferred-dark-theme": "dark", + "rustdoc-use-system-theme": "false", +} // We reload the page so the local storage settings are being used. reload: assert-css: ("body", { "background-color": "rgb(53, 53, 53)" }) assert-local-storage: { "rustdoc-theme": "dark" } // Now we go to the settings page. -click: "#settings-menu" -wait-for: ".settings" +goto: file://|DOC_PATH|/settings.html +wait-for: "#settings" // We change the theme to "light". click: "#theme-light" wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" }) @@ -18,7 +22,7 @@ assert-local-storage: { "rustdoc-theme": "light" } // We go back in history. history-go-back: // Confirm that we're not on the settings page. -assert-false: ".settings" +assert-false: "#settings" // Check that the current theme is still "light". assert-css: ("body", { "background-color": "rgb(255, 255, 255)" }) assert-local-storage: { "rustdoc-theme": "light" } From 2f074dee428889cbe3aa3cad7de8e87949b5c1fb Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume.gomez@huawei.com> Date: Fri, 29 Apr 2022 15:07:47 +0200 Subject: [PATCH 03/14] Extend settings test to ensure settings text is as expected --- src/test/rustdoc-gui/settings.goml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/test/rustdoc-gui/settings.goml b/src/test/rustdoc-gui/settings.goml index 45a7f8bcdde2a..6c4611b1cb2a6 100644 --- a/src/test/rustdoc-gui/settings.goml +++ b/src/test/rustdoc-gui/settings.goml @@ -36,3 +36,32 @@ click: "#settings-menu" wait-for: "#alternative-display #settings" assert: "#not-displayed #search" assert: "#main-content.hidden" + +// Now let's check the content of the settings menu. +local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"} +reload: +click: "#settings-menu" +wait-for: "#settings" + +// We check that the "Use system theme" is disabled. +assert-property: ("#use-system-theme", {"checked": "false"}) +assert: "//*[@class='setting-line']/*[text()='Use system theme']" +// Meaning that only the "theme" menu is showing up. +assert: ".setting-line:not(.hidden) #theme" +assert: ".setting-line.hidden #preferred-dark-theme" +assert: ".setting-line.hidden #preferred-light-theme" + +// We check that the correct theme is selected. +assert-property: ("#theme .choices #theme-dark", {"checked": "true"}) + +// We now switch the display. +click: "#use-system-theme" +// Wait for the hidden element to show up. +wait-for: ".setting-line:not(.hidden) #preferred-dark-theme" +assert: ".setting-line:not(.hidden) #preferred-light-theme" +// Check that the theme picking is hidden. +assert: ".setting-line.hidden #theme" + +// We check their text as well. +assert-text: ("#preferred-dark-theme .setting-name", "Preferred dark theme") +assert-text: ("#preferred-light-theme .setting-name", "Preferred light theme") From 336bb0afea102a0e4ec7f56c364c7cd0d2acb902 Mon Sep 17 00:00:00 2001 From: bjorn3 <bjorn3@users.noreply.github.com> Date: Sat, 30 Apr 2022 20:50:17 +0200 Subject: [PATCH 04/14] Rename run_lto_pass_manager to optimize_fat and remove thin parameter --- compiler/rustc_codegen_gcc/src/lib.rs | 10 +++++----- compiler/rustc_codegen_llvm/src/lib.rs | 17 ++++++++--------- compiler/rustc_codegen_ssa/src/traits/write.rs | 11 +++++------ 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 497a28354d813..2b960582879cb 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -229,6 +229,11 @@ impl WriteBackendMethods for GccCodegenBackend { Ok(()) } + fn optimize_fat(_cgcx: &CodegenContext<Self>, _module: &ModuleCodegen<Self::Module>, _config: &ModuleConfig) -> Result<(), FatalError> { + // TODO(antoyo) + Ok(()) + } + unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: &mut ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> { unimplemented!(); } @@ -245,11 +250,6 @@ impl WriteBackendMethods for GccCodegenBackend { unimplemented!(); } - fn run_lto_pass_manager(_cgcx: &CodegenContext<Self>, _module: &ModuleCodegen<Self::Module>, _config: &ModuleConfig, _thin: bool) -> Result<(), FatalError> { - // TODO(antoyo) - Ok(()) - } - fn run_link(cgcx: &CodegenContext<Self>, diag_handler: &Handler, modules: Vec<ModuleCodegen<Self::Module>>) -> Result<ModuleCodegen<Self::Module>, FatalError> { back::write::link(cgcx, diag_handler, modules) } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 3152c505af0e5..3fa906f497d11 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -210,6 +210,14 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> Result<(), FatalError> { back::write::optimize(cgcx, diag_handler, module, config) } + fn optimize_fat( + cgcx: &CodegenContext<Self>, + module: &ModuleCodegen<Self::Module>, + config: &ModuleConfig, + ) -> Result<(), FatalError> { + let diag_handler = cgcx.create_diag_handler(); + back::lto::run_pass_manager(cgcx, &diag_handler, module, config, false) + } unsafe fn optimize_thin( cgcx: &CodegenContext<Self>, thin: &mut ThinModule<Self>, @@ -230,15 +238,6 @@ impl WriteBackendMethods for LlvmCodegenBackend { fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) { (module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod())) } - fn run_lto_pass_manager( - cgcx: &CodegenContext<Self>, - module: &ModuleCodegen<Self::Module>, - config: &ModuleConfig, - thin: bool, - ) -> Result<(), FatalError> { - let diag_handler = cgcx.create_diag_handler(); - back::lto::run_pass_manager(cgcx, &diag_handler, module, config, thin) - } } unsafe impl Send for LlvmCodegenBackend {} // Llvm is on a per-thread basis diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 93fbee2b49bb5..4914de20b05b6 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -41,6 +41,11 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { module: &ModuleCodegen<Self::Module>, config: &ModuleConfig, ) -> Result<(), FatalError>; + fn optimize_fat( + cgcx: &CodegenContext<Self>, + llmod: &ModuleCodegen<Self::Module>, + config: &ModuleConfig, + ) -> Result<(), FatalError>; unsafe fn optimize_thin( cgcx: &CodegenContext<Self>, thin: &mut ThinModule<Self>, @@ -53,12 +58,6 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { ) -> Result<CompiledModule, FatalError>; fn prepare_thin(module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer); fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer); - fn run_lto_pass_manager( - cgcx: &CodegenContext<Self>, - llmod: &ModuleCodegen<Self::Module>, - config: &ModuleConfig, - thin: bool, - ) -> Result<(), FatalError>; } pub trait ThinBufferMethods: Send + Sync { From ee94ff254aceccd27919f26c02541277a3ca7dd7 Mon Sep 17 00:00:00 2001 From: bjorn3 <bjorn3@users.noreply.github.com> Date: Sat, 30 Apr 2022 20:51:17 +0200 Subject: [PATCH 05/14] Let LtoModuleCodegen::optimize take self by value --- compiler/rustc_codegen_gcc/src/lib.rs | 4 ++-- compiler/rustc_codegen_llvm/src/back/lto.rs | 4 ++-- compiler/rustc_codegen_llvm/src/lib.rs | 2 +- compiler/rustc_codegen_ssa/src/back/lto.rs | 13 ++++++------- compiler/rustc_codegen_ssa/src/back/write.rs | 2 +- compiler/rustc_codegen_ssa/src/traits/write.rs | 2 +- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 2b960582879cb..72da59a1bf808 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -213,7 +213,7 @@ impl WriteBackendMethods for GccCodegenBackend { unimplemented!(); } }; - Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: vec![] }) + Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: vec![] }) } fn run_thin_lto(_cgcx: &CodegenContext<Self>, _modules: Vec<(String, Self::ThinBuffer)>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError> { @@ -234,7 +234,7 @@ impl WriteBackendMethods for GccCodegenBackend { Ok(()) } - unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: &mut ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> { + unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> { unimplemented!(); } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 5c63bd8c1bd8d..570410caaabed 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -353,7 +353,7 @@ fn fat_lto( } } - Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: serialized_bitcode }) + Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: serialized_bitcode }) } crate struct Linker<'a>(&'a mut llvm::Linker<'a>); @@ -726,7 +726,7 @@ impl Drop for ThinBuffer { } pub unsafe fn optimize_thin_module( - thin_module: &mut ThinModule<LlvmCodegenBackend>, + thin_module: ThinModule<LlvmCodegenBackend>, cgcx: &CodegenContext<LlvmCodegenBackend>, ) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> { let diag_handler = cgcx.create_diag_handler(); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 3fa906f497d11..c3db6352a531c 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -220,7 +220,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { } unsafe fn optimize_thin( cgcx: &CodegenContext<Self>, - thin: &mut ThinModule<Self>, + thin: ThinModule<Self>, ) -> Result<ModuleCodegen<Self::Module>, FatalError> { back::lto::optimize_thin_module(thin, cgcx) } diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index d6ae689f254b1..6dc8cb5038e12 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -42,7 +42,7 @@ pub struct ThinShared<B: WriteBackendMethods> { pub enum LtoModuleCodegen<B: WriteBackendMethods> { Fat { - module: Option<ModuleCodegen<B::Module>>, + module: ModuleCodegen<B::Module>, _serialized_bitcode: Vec<SerializedModule<B::ModuleBuffer>>, }, @@ -64,19 +64,18 @@ impl<B: WriteBackendMethods> LtoModuleCodegen<B> { /// It's intended that the module returned is immediately code generated and /// dropped, and then this LTO module is dropped. pub unsafe fn optimize( - &mut self, + self, cgcx: &CodegenContext<B>, ) -> Result<ModuleCodegen<B::Module>, FatalError> { - match *self { - LtoModuleCodegen::Fat { ref mut module, .. } => { - let module = module.take().unwrap(); + match self { + LtoModuleCodegen::Fat { module, .. } => { { let config = cgcx.config(module.kind); - B::run_lto_pass_manager(cgcx, &module, config, false)?; + B::optimize_fat(cgcx, &module, config)?; } Ok(module) } - LtoModuleCodegen::Thin(ref mut thin) => B::optimize_thin(cgcx, thin), + LtoModuleCodegen::Thin(thin) => B::optimize_thin(cgcx, thin), } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 98dc5fe8d6424..88293dec01cac 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -889,7 +889,7 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>( fn execute_lto_work_item<B: ExtraBackendMethods>( cgcx: &CodegenContext<B>, - mut module: lto::LtoModuleCodegen<B>, + module: lto::LtoModuleCodegen<B>, module_config: &ModuleConfig, ) -> Result<WorkItemResult<B>, FatalError> { let module = unsafe { module.optimize(cgcx)? }; diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 4914de20b05b6..ed0f659180926 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -48,7 +48,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { ) -> Result<(), FatalError>; unsafe fn optimize_thin( cgcx: &CodegenContext<Self>, - thin: &mut ThinModule<Self>, + thin: ThinModule<Self>, ) -> Result<ModuleCodegen<Self::Module>, FatalError>; unsafe fn codegen( cgcx: &CodegenContext<Self>, From fab72301d9089761766b389df209986c2bc38e5d Mon Sep 17 00:00:00 2001 From: bjorn3 <bjorn3@users.noreply.github.com> Date: Sat, 30 Apr 2022 20:58:42 +0200 Subject: [PATCH 06/14] Remove config parameter of optimize_fat and avoid interior mutability for module --- compiler/rustc_codegen_gcc/src/lib.rs | 2 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 13 +++++-------- compiler/rustc_codegen_llvm/src/lib.rs | 5 ++--- compiler/rustc_codegen_ssa/src/back/lto.rs | 5 ++--- compiler/rustc_codegen_ssa/src/traits/write.rs | 3 +-- 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 72da59a1bf808..684b845c38a29 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -229,7 +229,7 @@ impl WriteBackendMethods for GccCodegenBackend { Ok(()) } - fn optimize_fat(_cgcx: &CodegenContext<Self>, _module: &ModuleCodegen<Self::Module>, _config: &ModuleConfig) -> Result<(), FatalError> { + fn optimize_fat(_cgcx: &CodegenContext<Self>, _module: &mut ModuleCodegen<Self::Module>) -> Result<(), FatalError> { // TODO(antoyo) Ok(()) } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 570410caaabed..b5b2a27d2378d 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -6,9 +6,7 @@ use crate::llvm::{self, build_string, False, True}; use crate::{llvm_util, LlvmCodegenBackend, ModuleLlvm}; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::symbol_export; -use rustc_codegen_ssa::back::write::{ - CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig, -}; +use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, TargetMachineFactoryConfig}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind}; use rustc_data_structures::fx::FxHashMap; @@ -578,11 +576,11 @@ fn thin_lto( pub(crate) fn run_pass_manager( cgcx: &CodegenContext<LlvmCodegenBackend>, diag_handler: &Handler, - module: &ModuleCodegen<ModuleLlvm>, - config: &ModuleConfig, + module: &mut ModuleCodegen<ModuleLlvm>, thin: bool, ) -> Result<(), FatalError> { let _timer = cgcx.prof.extra_verbose_generic_activity("LLVM_lto_optimize", &*module.name); + let config = cgcx.config(module.kind); // Now we have one massive module inside of llmod. Time to run the // LTO-specific optimization passes that LLVM provides. @@ -743,7 +741,7 @@ pub unsafe fn optimize_thin_module( // that LLVM Context and Module. let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); let llmod_raw = parse_module(llcx, module_name, thin_module.data(), &diag_handler)? as *const _; - let module = ModuleCodegen { + let mut module = ModuleCodegen { module_llvm: ModuleLlvm { llmod_raw, llcx, tm }, name: thin_module.name().to_string(), kind: ModuleKind::Regular, @@ -859,8 +857,7 @@ pub unsafe fn optimize_thin_module( // little differently. { info!("running thin lto passes over {}", module.name); - let config = cgcx.config(module.kind); - run_pass_manager(cgcx, &diag_handler, &module, config, true)?; + run_pass_manager(cgcx, &diag_handler, &mut module, true)?; save_temp_bitcode(cgcx, &module, "thin-lto-after-pm"); } } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index c3db6352a531c..b48c738124d04 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -212,11 +212,10 @@ impl WriteBackendMethods for LlvmCodegenBackend { } fn optimize_fat( cgcx: &CodegenContext<Self>, - module: &ModuleCodegen<Self::Module>, - config: &ModuleConfig, + module: &mut ModuleCodegen<Self::Module>, ) -> Result<(), FatalError> { let diag_handler = cgcx.create_diag_handler(); - back::lto::run_pass_manager(cgcx, &diag_handler, module, config, false) + back::lto::run_pass_manager(cgcx, &diag_handler, module, false) } unsafe fn optimize_thin( cgcx: &CodegenContext<Self>, diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index 6dc8cb5038e12..3c5b0e5cc43ba 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -68,10 +68,9 @@ impl<B: WriteBackendMethods> LtoModuleCodegen<B> { cgcx: &CodegenContext<B>, ) -> Result<ModuleCodegen<B::Module>, FatalError> { match self { - LtoModuleCodegen::Fat { module, .. } => { + LtoModuleCodegen::Fat { mut module, .. } => { { - let config = cgcx.config(module.kind); - B::optimize_fat(cgcx, &module, config)?; + B::optimize_fat(cgcx, &mut module)?; } Ok(module) } diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index ed0f659180926..e54ec34f1ce37 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -43,8 +43,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { ) -> Result<(), FatalError>; fn optimize_fat( cgcx: &CodegenContext<Self>, - llmod: &ModuleCodegen<Self::Module>, - config: &ModuleConfig, + llmod: &mut ModuleCodegen<Self::Module>, ) -> Result<(), FatalError>; unsafe fn optimize_thin( cgcx: &CodegenContext<Self>, From 78c65a52db0016f489862ecea92a09ad66a2b960 Mon Sep 17 00:00:00 2001 From: bjorn3 <bjorn3@users.noreply.github.com> Date: Sat, 30 Apr 2022 21:20:08 +0200 Subject: [PATCH 07/14] Merge new_metadata into codegen_allocator --- compiler/rustc_codegen_gcc/src/lib.rs | 12 +++++------- compiler/rustc_codegen_llvm/src/lib.rs | 13 ++++++------- compiler/rustc_codegen_ssa/src/back/lto.rs | 4 +--- compiler/rustc_codegen_ssa/src/base.rs | 11 ++--------- compiler/rustc_codegen_ssa/src/traits/backend.rs | 4 +--- 5 files changed, 15 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 684b845c38a29..58996a9db78ad 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -139,14 +139,12 @@ impl CodegenBackend for GccCodegenBackend { } impl ExtraBackendMethods for GccCodegenBackend { - fn new_metadata<'tcx>(&self, _tcx: TyCtxt<'tcx>, _mod_name: &str) -> Self::Module { - GccContext { + fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) -> Self::Module { + let mut mods = GccContext { context: Context::default(), - } - } - - fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, mods: &mut Self::Module, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) { - unsafe { allocator::codegen(tcx, mods, module_name, kind, has_alloc_error_handler) } + }; + unsafe { allocator::codegen(tcx, &mut mods, module_name, kind, has_alloc_error_handler); } + mods } fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index b48c738124d04..b035923956954 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -104,19 +104,18 @@ impl Drop for TimeTraceProfiler { } impl ExtraBackendMethods for LlvmCodegenBackend { - fn new_metadata(&self, tcx: TyCtxt<'_>, mod_name: &str) -> ModuleLlvm { - ModuleLlvm::new_metadata(tcx, mod_name) - } - fn codegen_allocator<'tcx>( &self, tcx: TyCtxt<'tcx>, - module_llvm: &mut ModuleLlvm, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool, - ) { - unsafe { allocator::codegen(tcx, module_llvm, module_name, kind, has_alloc_error_handler) } + ) -> ModuleLlvm { + let mut module_llvm = ModuleLlvm::new_metadata(tcx, module_name); + unsafe { + allocator::codegen(tcx, &mut module_llvm, module_name, kind, has_alloc_error_handler); + } + module_llvm } fn compile_codegen_unit( &self, diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index 3c5b0e5cc43ba..cb6244050df24 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -69,9 +69,7 @@ impl<B: WriteBackendMethods> LtoModuleCodegen<B> { ) -> Result<ModuleCodegen<B::Module>, FatalError> { match self { LtoModuleCodegen::Fat { mut module, .. } => { - { - B::optimize_fat(cgcx, &mut module)?; - } + B::optimize_fat(cgcx, &mut module)?; Ok(module) } LtoModuleCodegen::Thin(thin) => B::optimize_thin(cgcx, thin), diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 019c9c179d8e1..5bc95614c197c 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -575,15 +575,8 @@ pub fn codegen_crate<B: ExtraBackendMethods>( } else if let Some(kind) = tcx.allocator_kind(()) { let llmod_id = cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); - let mut module_llvm = backend.new_metadata(tcx, &llmod_id); - tcx.sess.time("write_allocator_module", || { - backend.codegen_allocator( - tcx, - &mut module_llvm, - &llmod_id, - kind, - tcx.lang_items().oom().is_some(), - ) + let module_llvm = tcx.sess.time("write_allocator_module", || { + backend.codegen_allocator(tcx, &llmod_id, kind, tcx.lang_items().oom().is_some()) }); Some(ModuleCodegen { name: llmod_id, module_llvm, kind: ModuleKind::Allocator }) diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 856b774258316..1e53c73d1bb4a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -114,15 +114,13 @@ pub trait CodegenBackend { } pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Send + Sync { - fn new_metadata(&self, sess: TyCtxt<'_>, mod_name: &str) -> Self::Module; fn codegen_allocator<'tcx>( &self, tcx: TyCtxt<'tcx>, - module_llvm: &mut Self::Module, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool, - ); + ) -> Self::Module; /// This generates the codegen unit and returns it along with /// a `u64` giving an estimate of the unit's processing cost. fn compile_codegen_unit( From 685f66b6b1be089e3a365a43e5a4b4ac8dd78ec7 Mon Sep 17 00:00:00 2001 From: Badel2 <2badel2@gmail.com> Date: Sat, 30 Apr 2022 22:22:20 +0200 Subject: [PATCH 08/14] Use source callsite in check_argument_types suggestion This makes the "remove extra arguement" suggestion valid when the function argument is a macro --- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 2 +- .../ui/typeck/remove-extra-argument.fixed | 9 +++++++++ src/test/ui/typeck/remove-extra-argument.rs | 9 +++++++++ .../ui/typeck/remove-extra-argument.stderr | 19 +++++++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/typeck/remove-extra-argument.fixed create mode 100644 src/test/ui/typeck/remove-extra-argument.rs create mode 100644 src/test/ui/typeck/remove-extra-argument.stderr diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 616aa11f00a6b..7e225bf43b900 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -980,7 +980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); for (idx, arg) in matched_inputs.iter().enumerate() { let suggestion_text = if let Some(arg) = arg { - let arg_span = provided_args[*arg].span; + let arg_span = provided_args[*arg].span.source_callsite(); let arg_text = source_map.span_to_snippet(arg_span).unwrap(); arg_text } else { diff --git a/src/test/ui/typeck/remove-extra-argument.fixed b/src/test/ui/typeck/remove-extra-argument.fixed new file mode 100644 index 0000000000000..a9338c76cdc88 --- /dev/null +++ b/src/test/ui/typeck/remove-extra-argument.fixed @@ -0,0 +1,9 @@ +// run-rustfix +// Check that the HELP suggestion is `l(vec![])` instead of `l($crate::vec::Vec::new())` +fn l(_a: Vec<u8>) {} + +fn main() { + l(vec![]) + //~^ ERROR this function takes 1 argument but 2 arguments were supplied + //~| HELP remove the extra argument +} diff --git a/src/test/ui/typeck/remove-extra-argument.rs b/src/test/ui/typeck/remove-extra-argument.rs new file mode 100644 index 0000000000000..659cb8b267fd5 --- /dev/null +++ b/src/test/ui/typeck/remove-extra-argument.rs @@ -0,0 +1,9 @@ +// run-rustfix +// Check that the HELP suggestion is `l(vec![])` instead of `l($crate::vec::Vec::new())` +fn l(_a: Vec<u8>) {} + +fn main() { + l(vec![], vec![]) + //~^ ERROR this function takes 1 argument but 2 arguments were supplied + //~| HELP remove the extra argument +} diff --git a/src/test/ui/typeck/remove-extra-argument.stderr b/src/test/ui/typeck/remove-extra-argument.stderr new file mode 100644 index 0000000000000..815297765c18c --- /dev/null +++ b/src/test/ui/typeck/remove-extra-argument.stderr @@ -0,0 +1,19 @@ +error[E0061]: this function takes 1 argument but 2 arguments were supplied + --> $DIR/remove-extra-argument.rs:6:5 + | +LL | l(vec![], vec![]) + | ^ ------ argument unexpected + | +note: function defined here + --> $DIR/remove-extra-argument.rs:3:4 + | +LL | fn l(_a: Vec<u8>) {} + | ^ ----------- +help: remove the extra argument + | +LL | l(vec![]) + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0061`. From b37fb23d9f6946a9b9d7b43590105ed22a081940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= <tomasz.miasko@gmail.com> Date: Sun, 1 May 2022 00:00:00 +0000 Subject: [PATCH 09/14] Update `RValue::Discriminant` documentation `RValue::Discriminant` returns zero for types without discriminant. This guarantee is already documented for `discriminant_value` intrinsics which is implemented in terms of `RValue::Discriminant`. --- compiler/rustc_middle/src/mir/mod.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 45999f8765897..5e493b52ed641 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2559,16 +2559,12 @@ pub enum Rvalue<'tcx> { UnaryOp(UnOp, Operand<'tcx>), /// Computes the discriminant of the place, returning it as an integer of type - /// [`discriminant_ty`]. + /// [`discriminant_ty`]. Returns zero for types without discriminant. /// /// The validity requirements for the underlying value are undecided for this rvalue, see /// [#91095]. Note too that the value of the discriminant is not the same thing as the /// variant index; use [`discriminant_for_variant`] to convert. /// - /// For types defined in the source code as enums, this is well behaved. This is also well - /// formed for other types, but yields no particular value - there is no reason it couldn't be - /// defined to yield eg zero though. - /// /// [`discriminant_ty`]: crate::ty::Ty::discriminant_ty /// [#91095]: https://github.com/rust-lang/rust/issues/91095 /// [`discriminant_for_variant`]: crate::ty::Ty::discriminant_for_variant From 73688e40216f72a57577b39acfb9180322a41362 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez <guillaume.gomez@huawei.com> Date: Sun, 1 May 2022 21:22:38 +0200 Subject: [PATCH 10/14] * Add documentation for settings page rendering functions. * Improve code. * Fix some documentation argument types. * Make settings order the same as before this PR. * Change timeout to 0 so that browser will render it as fast as possible. --- src/librustdoc/html/static/js/main.js | 12 +-- src/librustdoc/html/static/js/settings.js | 108 ++++++++++++---------- 2 files changed, 65 insertions(+), 55 deletions(-) diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 7ac835b327034..f20061c65dd1b 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -206,8 +206,8 @@ window.hideSettings = function() { * This function inserts `newNode` after `referenceNode`. It doesn't work if `referenceNode` * doesn't have a parent node. * - * @param {DOM} newNode - * @param {DOM} referenceNode + * @param {HTMLElement} newNode + * @param {HTMLElement} referenceNode */ function insertAfter(newNode, referenceNode) { referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); @@ -237,7 +237,7 @@ function getOrCreateSection(id, classes) { /** * Returns the `<section>` element which contains the displayed element. * - * @return {DOM} + * @return {HTMLElement} */ function getAlternativeDisplayElem() { return getOrCreateSection(ALTERNATIVE_DISPLAY_ID, "content hidden"); @@ -246,7 +246,7 @@ function getAlternativeDisplayElem() { /** * Returns the `<section>` element which contains the not-displayed elements. * - * @return {DOM} + * @return {HTMLElement} */ function getNotDisplayedElem() { return getOrCreateSection(NOT_DISPLAYED_ID, "hidden"); @@ -255,11 +255,11 @@ function getNotDisplayedElem() { /** * To nicely switch between displayed "extra" elements (such as search results or settings menu) * and to alternate between the displayed and not displayed elements, we hold them in two different - * `<section>` elements. They work in pair: one hold the not displayed elements while the other + * `<section>` elements. They work in pair: one holds the hidden elements while the other * contains the displayed element (there can be only one at the same time!). So basically, we switch * elements between the two `<section>` elements. * - * @param {DOM} elemToDisplay + * @param {HTMLElement} elemToDisplay */ function switchDisplayedElement(elemToDisplay) { const el = getAlternativeDisplayElem(); diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index bdc1162a7c071..a2f8d56fb320b 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -96,10 +96,19 @@ }); } - function buildSettings(settings) { + /** + * This function builds the sections inside the "settings page". It takes a `settings` list + * as argument which describes each setting and how to render it. It returns a string + * representing the raw HTML. + * + * @param {Array<Object>} settings + * + * @return {string} + */ + function buildSettingsPageSections(settings) { let output = ""; - onEach(settings, function(setting) { + for (const setting of settings) { output += `<div class="setting-line">`; const js_data_name = setting["js_name"]; const setting_name = setting["name"]; @@ -130,47 +139,16 @@ <div>${setting_name}</div>`; } output += "</div>"; - }); - return output; - } - - function buildSettingsPage(settings) { - // First, we add the settings.css file. - loadCss("settings"); - - // Then we build the DOM. - const el = document.createElement("section"); - el.id = "settings"; - let innerHTML = ` - <div class="main-heading"> - <h1 class="fqn"> - <span class="in-band">Rustdoc settings</span> - </h1> - <span class="out-of-band">`; - - if (isSettingsPage) { - innerHTML += - `<a id="back" href="javascript:void(0)" onclick="history.back();">Back</a>`; - } else { - innerHTML += - `<a id="back" href="javascript:void(0)" onclick="switchDisplayedElement(null);">\ - Back</a>`; - } - innerHTML += `</span> - </div> - <div class="settings">${buildSettings(settings)}</div>`; - - el.innerHTML = innerHTML; - - if (isSettingsPage) { - document.getElementById(MAIN_ID).appendChild(el); - } else { - getNotDisplayedElem().appendChild(el); } - return el; + return output; } - function createSettingsPage() { + /** + * This function builds the "settings page" and returns the generated HTML element. + * + * @return {HTMLElement} + */ + function buildSettingsPage() { const themes = getVar("themes").split(","); const settings = [ { @@ -184,18 +162,18 @@ "default": "light", "options": themes, }, - { - "name": "Preferred dark theme", - "js_name": "preferred-dark-theme", - "default": "dark", - "options": themes, - }, { "name": "Preferred light theme", "js_name": "preferred-light-theme", "default": "light", "options": themes, }, + { + "name": "Preferred dark theme", + "js_name": "preferred-dark-theme", + "default": "dark", + "options": themes, + }, { "name": "Auto-hide item contents for large items", "js_name": "auto-hide-large-items", @@ -228,10 +206,42 @@ }, ]; - return buildSettingsPage(settings); + // First, we add the settings.css file. + loadCss("settings"); + + // Then we build the DOM. + const el = document.createElement("section"); + el.id = "settings"; + let innerHTML = ` + <div class="main-heading"> + <h1 class="fqn"> + <span class="in-band">Rustdoc settings</span> + </h1> + <span class="out-of-band">`; + + if (isSettingsPage) { + innerHTML += + `<a id="back" href="javascript:void(0)" onclick="history.back();">Back</a>`; + } else { + innerHTML += + `<a id="back" href="javascript:void(0)" onclick="switchDisplayedElement(null);">\ + Back</a>`; + } + innerHTML += `</span> + </div> + <div class="settings">${buildSettingsPageSections(settings)}</div>`; + + el.innerHTML = innerHTML; + + if (isSettingsPage) { + document.getElementById(MAIN_ID).appendChild(el); + } else { + getNotDisplayedElem().appendChild(el); + } + return el; } - const settingsMenu = createSettingsPage(); + const settingsMenu = buildSettingsPage(); if (isSettingsPage) { // We replace the existing "onclick" callback to do nothing if clicked. @@ -261,5 +271,5 @@ if (!isSettingsPage) { switchDisplayedElement(settingsMenu); } - }, 10); + }, 0); })(); From 18b12fa993312c41b10487efa85fed95ad1af372 Mon Sep 17 00:00:00 2001 From: Yuki Okushi <jtitor@2k36.org> Date: Mon, 2 May 2022 06:43:17 +0900 Subject: [PATCH 11/14] Add a regression test for #92305 --- src/test/ui/impl-trait/issues/issue-92305.rs | 15 +++++++++ .../ui/impl-trait/issues/issue-92305.stderr | 32 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/test/ui/impl-trait/issues/issue-92305.rs create mode 100644 src/test/ui/impl-trait/issues/issue-92305.stderr diff --git a/src/test/ui/impl-trait/issues/issue-92305.rs b/src/test/ui/impl-trait/issues/issue-92305.rs new file mode 100644 index 0000000000000..1518c116b3185 --- /dev/null +++ b/src/test/ui/impl-trait/issues/issue-92305.rs @@ -0,0 +1,15 @@ +// edition:2021 + +use std::iter; + +fn f<T>(data: &[T]) -> impl Iterator<Item = Vec> { + //~^ ERROR: missing generics for struct `Vec` [E0107] + iter::empty() //~ ERROR: type annotations needed [E0282] +} + +fn g<T>(data: &[T], target: T) -> impl Iterator<Item = Vec<T>> { + //~^ ERROR: type annotations needed [E0282] + f(data).filter(|x| x == target) +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issues/issue-92305.stderr b/src/test/ui/impl-trait/issues/issue-92305.stderr new file mode 100644 index 0000000000000..c4fc49225e9a0 --- /dev/null +++ b/src/test/ui/impl-trait/issues/issue-92305.stderr @@ -0,0 +1,32 @@ +error[E0107]: missing generics for struct `Vec` + --> $DIR/issue-92305.rs:5:45 + | +LL | fn f<T>(data: &[T]) -> impl Iterator<Item = Vec> { + | ^^^ expected at least 1 generic argument + | +note: struct defined here, with at least 1 generic parameter: `T` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + | +LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> { + | ^^^ - +help: add missing generic argument + | +LL | fn f<T>(data: &[T]) -> impl Iterator<Item = Vec<T>> { + | ~~~~~~ + +error[E0282]: type annotations needed + --> $DIR/issue-92305.rs:7:5 + | +LL | iter::empty() + | ^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the function `empty` + +error[E0282]: type annotations needed + --> $DIR/issue-92305.rs:10:35 + | +LL | fn g<T>(data: &[T], target: T) -> impl Iterator<Item = Vec<T>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0107, E0282. +For more information about an error, try `rustc --explain E0107`. From 6c7f4dee8f26a6bbe2970d735f8dc2cbaa0a5eef Mon Sep 17 00:00:00 2001 From: Ken Matsui <26405363+ken-matsui@users.noreply.github.com> Date: Mon, 2 May 2022 18:14:43 +0900 Subject: [PATCH 12/14] Fix invalid keyword order for function declarations --- compiler/rustc_parse/src/parser/item.rs | 4 ++-- src/test/ui/async-await/no-async-const.stderr | 2 +- src/test/ui/async-await/no-unsafe-async.stderr | 4 ++-- src/test/ui/fn/keyword-order.rs | 6 ++++++ src/test/ui/fn/keyword-order.stderr | 16 ++++++++++++++++ src/test/ui/parser/issues/issue-19398.stderr | 2 +- .../issue-87217-keyword-order/several-kw-jump.rs | 2 +- .../several-kw-jump.stderr | 2 +- .../issue-87217-keyword-order/wrong-async.rs | 2 +- .../issue-87217-keyword-order/wrong-async.stderr | 2 +- .../issue-87217-keyword-order/wrong-const.rs | 2 +- .../issue-87217-keyword-order/wrong-const.stderr | 2 +- .../issue-87217-keyword-order/wrong-unsafe.rs | 2 +- .../wrong-unsafe.stderr | 2 +- 14 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 src/test/ui/fn/keyword-order.rs create mode 100644 src/test/ui/fn/keyword-order.stderr diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 10f1daf112951..77a03428c1664 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1020,7 +1020,7 @@ impl<'a> Parser<'a> { &format!("`{}` must come before `{}`", invalid_qual, current_qual), format!("{} {}", invalid_qual, current_qual), Applicability::MachineApplicable, - ).note("keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`"); + ).note("keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`"); } } Err(err) @@ -2086,7 +2086,7 @@ impl<'a> Parser<'a> { &format!("`{misplaced_qual}` must come before `{current_qual}`"), format!("{misplaced_qual} {current_qual}"), Applicability::MachineApplicable, - ).note("keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`"); + ).note("keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`"); } } // Recover incorrect visibility order such as `async pub` diff --git a/src/test/ui/async-await/no-async-const.stderr b/src/test/ui/async-await/no-async-const.stderr index ae13b90c3cfc0..a51dc88a4eded 100644 --- a/src/test/ui/async-await/no-async-const.stderr +++ b/src/test/ui/async-await/no-async-const.stderr @@ -7,7 +7,7 @@ LL | pub async const fn x() {} | | expected one of `extern`, `fn`, or `unsafe` | help: `const` must come before `async`: `const async` | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to previous error diff --git a/src/test/ui/async-await/no-unsafe-async.stderr b/src/test/ui/async-await/no-unsafe-async.stderr index 0c362052501ed..f23d17d6bfa57 100644 --- a/src/test/ui/async-await/no-unsafe-async.stderr +++ b/src/test/ui/async-await/no-unsafe-async.stderr @@ -12,7 +12,7 @@ LL | unsafe async fn g() {} LL | } | - the item list ends here | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: expected one of `extern` or `fn`, found keyword `async` --> $DIR/no-unsafe-async.rs:11:8 @@ -23,7 +23,7 @@ LL | unsafe async fn f() {} | | expected one of `extern` or `fn` | help: `async` must come before `unsafe`: `async unsafe` | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to 2 previous errors diff --git a/src/test/ui/fn/keyword-order.rs b/src/test/ui/fn/keyword-order.rs new file mode 100644 index 0000000000000..8a21db6733352 --- /dev/null +++ b/src/test/ui/fn/keyword-order.rs @@ -0,0 +1,6 @@ +// edition:2018 + +default pub const async unsafe extern fn err() {} //~ ERROR `default` is not followed by an item +//~^ ERROR expected item, found keyword `pub` + +pub default const async unsafe extern fn ok() {} diff --git a/src/test/ui/fn/keyword-order.stderr b/src/test/ui/fn/keyword-order.stderr new file mode 100644 index 0000000000000..d3b140c852860 --- /dev/null +++ b/src/test/ui/fn/keyword-order.stderr @@ -0,0 +1,16 @@ +error: `default` is not followed by an item + --> $DIR/keyword-order.rs:3:1 + | +LL | default pub const async unsafe extern fn err() {} + | ^^^^^^^ the `default` qualifier + | + = note: only `fn`, `const`, `type`, or `impl` items may be prefixed by `default` + +error: expected item, found keyword `pub` + --> $DIR/keyword-order.rs:3:9 + | +LL | default pub const async unsafe extern fn err() {} + | ^^^ expected item + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/parser/issues/issue-19398.stderr b/src/test/ui/parser/issues/issue-19398.stderr index f9c3ca763f26e..bbd85374b4bcf 100644 --- a/src/test/ui/parser/issues/issue-19398.stderr +++ b/src/test/ui/parser/issues/issue-19398.stderr @@ -12,7 +12,7 @@ LL | LL | } | - the item list ends here | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to previous error diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.rs b/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.rs index 86fdb78cce8ab..bbebc99e94b82 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.rs +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.rs @@ -11,4 +11,4 @@ async unsafe const fn test() {} //~| NOTE expected one of `extern` or `fn` //~| HELP `const` must come before `async unsafe` //~| SUGGESTION const async unsafe -//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` +//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.stderr b/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.stderr index 65cce77be896b..f455caba158c7 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.stderr +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.stderr @@ -7,7 +7,7 @@ LL | async unsafe const fn test() {} | | expected one of `extern` or `fn` | help: `const` must come before `async unsafe`: `const async unsafe` | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to previous error diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.rs b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.rs index edfb330d6713a..4ff4cf5c8ca87 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.rs +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.rs @@ -11,4 +11,4 @@ unsafe async fn test() {} //~| NOTE expected one of `extern` or `fn` //~| HELP `async` must come before `unsafe` //~| SUGGESTION async unsafe -//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` +//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.stderr b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.stderr index 3acd9e4400432..e9eb14bf00e77 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.stderr +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.stderr @@ -7,7 +7,7 @@ LL | unsafe async fn test() {} | | expected one of `extern` or `fn` | help: `async` must come before `unsafe`: `async unsafe` | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to previous error diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.rs b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.rs index abd692b80d54b..2f5fbc513ee36 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.rs +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.rs @@ -11,4 +11,4 @@ unsafe const fn test() {} //~| NOTE expected one of `extern` or `fn` //~| HELP `const` must come before `unsafe` //~| SUGGESTION const unsafe -//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` +//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.stderr b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.stderr index 9a3e07b1e87f6..0d2bc3472965f 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.stderr +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.stderr @@ -7,7 +7,7 @@ LL | unsafe const fn test() {} | | expected one of `extern` or `fn` | help: `const` must come before `unsafe`: `const unsafe` | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to previous error diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.rs b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.rs index 7f0761e99386a..df2412e3e9b32 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.rs +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.rs @@ -11,4 +11,4 @@ extern unsafe fn test() {} //~| NOTE expected `fn` //~| HELP `unsafe` must come before `extern` //~| SUGGESTION unsafe extern -//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` +//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.stderr b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.stderr index 395ee9fedbc07..4224713ccb53a 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.stderr +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.stderr @@ -7,7 +7,7 @@ LL | extern unsafe fn test() {} | | expected `fn` | help: `unsafe` must come before `extern`: `unsafe extern` | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to previous error From 0349f8bd79eb2b09884d0054a9012d7099eef5b9 Mon Sep 17 00:00:00 2001 From: Oli Scherer <git-spam-no-reply9815368754983@oli-obk.de> Date: Mon, 2 May 2022 15:36:48 +0000 Subject: [PATCH 13/14] Use a yes/no enum instead of a bool. The bool's meaning wasn't obvious to me at some call sites. --- compiler/rustc_resolve/src/ident.rs | 4 +- compiler/rustc_resolve/src/late.rs | 74 +++++++++++++++++++---------- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 18ce359524da2..baaab33d71f50 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1179,7 +1179,7 @@ impl<'a> Resolver<'a> { ConstantItemRibKind(trivial, _) => { let features = self.session.features_untracked(); // HACK(min_const_generics): We currently only allow `N` or `{ N }`. - if !(trivial || features.generic_const_exprs) { + if !(trivial == HasGenericParams::Yes || features.generic_const_exprs) { // HACK(min_const_generics): If we encounter `Self` in an anonymous constant // we can't easily tell if it's generic at this stage, so we instead remember // this and then enforce the self type to be concrete later on. @@ -1267,7 +1267,7 @@ impl<'a> Resolver<'a> { ConstantItemRibKind(trivial, _) => { let features = self.session.features_untracked(); // HACK(min_const_generics): We currently only allow `N` or `{ N }`. - if !(trivial || features.generic_const_exprs) { + if !(trivial == HasGenericParams::Yes || features.generic_const_exprs) { if let Some(span) = finalize { self.report_error( span, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index ca89f61032221..8b065fb036b0f 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -94,6 +94,12 @@ crate enum HasGenericParams { No, } +impl HasGenericParams { + fn force_yes_if(self, b: bool) -> Self { + if b { Self::Yes } else { self } + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq)] crate enum ConstantItemKind { Const, @@ -125,9 +131,9 @@ crate enum RibKind<'a> { /// We're in a constant item. Can't refer to dynamic stuff. /// - /// The `bool` indicates if this constant may reference generic parameters - /// and is used to only allow generic parameters to be used in trivial constant expressions. - ConstantItemRibKind(bool, Option<(Ident, ConstantItemKind)>), + /// The item may reference generic parameters in trivial constant expressions. + /// All other constants aren't allowed to use generic params at all. + ConstantItemRibKind(HasGenericParams, Option<(Ident, ConstantItemKind)>), /// We passed through a module. ModuleRibKind(Module<'a>), @@ -826,19 +832,24 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // Note that we might not be inside of an repeat expression here, // but considering that `IsRepeatExpr` is only relevant for // non-trivial constants this is doesn't matter. - self.with_constant_rib(IsRepeatExpr::No, true, None, |this| { - this.smart_resolve_path( - ty.id, - qself.as_ref(), - path, - PathSource::Expr(None), - ); - - if let Some(ref qself) = *qself { - this.visit_ty(&qself.ty); - } - this.visit_path(path, ty.id); - }); + self.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + None, + |this| { + this.smart_resolve_path( + ty.id, + qself.as_ref(), + path, + PathSource::Expr(None), + ); + + if let Some(ref qself) = *qself { + this.visit_ty(&qself.ty); + } + this.visit_path(path, ty.id); + }, + ); self.diagnostic_metadata.currently_processing_generics = prev; return; @@ -1688,7 +1699,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // not used as part of the type system, this is far less surprising. this.with_constant_rib( IsRepeatExpr::No, - true, + HasGenericParams::Yes, None, |this| this.visit_expr(expr), ); @@ -1767,7 +1778,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // so it doesn't matter whether this is a trivial constant. this.with_constant_rib( IsRepeatExpr::No, - true, + HasGenericParams::Yes, Some((item.ident, constant_item_kind)), |this| this.visit_expr(expr), ); @@ -1913,20 +1924,23 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Note that we intentionally still forbid `[0; N + 1]` during // name resolution so that we don't extend the future // compat lint to new cases. + #[instrument(level = "debug", skip(self, f))] fn with_constant_rib( &mut self, is_repeat: IsRepeatExpr, - is_trivial: bool, + may_use_generics: HasGenericParams, item: Option<(Ident, ConstantItemKind)>, f: impl FnOnce(&mut Self), ) { - debug!("with_constant_rib: is_repeat={:?} is_trivial={}", is_repeat, is_trivial); - self.with_rib(ValueNS, ConstantItemRibKind(is_trivial, item), |this| { + self.with_rib(ValueNS, ConstantItemRibKind(may_use_generics, item), |this| { this.with_rib( TypeNS, - ConstantItemRibKind(is_repeat == IsRepeatExpr::Yes || is_trivial, item), + ConstantItemRibKind( + may_use_generics.force_yes_if(is_repeat == IsRepeatExpr::Yes), + item, + ), |this| { - this.with_label_rib(ConstantItemRibKind(is_trivial, item), f); + this.with_label_rib(ConstantItemRibKind(may_use_generics, item), f); }, ) }); @@ -2068,7 +2082,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // not used as part of the type system, this is far less surprising. this.with_constant_rib( IsRepeatExpr::No, - true, + HasGenericParams::Yes, None, |this| { visit::walk_assoc_item( @@ -3081,7 +3095,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { debug!("resolve_anon_const {:?} is_repeat: {:?}", constant, is_repeat); self.with_constant_rib( is_repeat, - constant.value.is_potential_trivial_const_param(), + if constant.value.is_potential_trivial_const_param() { + HasGenericParams::Yes + } else { + HasGenericParams::No + }, None, |this| visit::walk_anon_const(this, constant), ); @@ -3184,7 +3202,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if const_args.contains(&idx) { self.with_constant_rib( IsRepeatExpr::No, - argument.is_potential_trivial_const_param(), + if argument.is_potential_trivial_const_param() { + HasGenericParams::Yes + } else { + HasGenericParams::No + }, None, |this| { this.resolve_expr(argument, None); From 7790b6e1c075a29274b4ee1e598b2f21bf363699 Mon Sep 17 00:00:00 2001 From: Esteban Kuber <esteban@kuber.com.ar> Date: Mon, 2 May 2022 19:11:03 +0000 Subject: [PATCH 14/14] Mitigate impact of subtle invalid call suggestion logic There's some subtle interaction between inferred expressions being passed as an argument to fn calls with fewer than expected arguments. To avoid the ICE, I'm changing indexing operations with `.get(idx)`, but the underlying logic still needs to be audited as it was written with the assumption that `final_arg_types` and `provided_args` have the right length. Address 96638. --- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 32 ++++++++++++------- .../ui/argument-suggestions/issue-96638.rs | 9 ++++++ .../argument-suggestions/issue-96638.stderr | 19 +++++++++++ 3 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 src/test/ui/argument-suggestions/issue-96638.rs create mode 100644 src/test/ui/argument-suggestions/issue-96638.stderr diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 616aa11f00a6b..cd3f03340e9de 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -429,9 +429,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { errors.drain_filter(|error| { let Error::Invalid(input_idx, Compatibility::Incompatible(error)) = error else { return false }; let expected_ty = expected_input_tys[*input_idx]; - let provided_ty = final_arg_types[*input_idx].map(|ty| ty.0).unwrap(); + let Some(Some((provided_ty, _))) = final_arg_types.get(*input_idx) else { return false }; let cause = &self.misc(provided_args[*input_idx].span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = TypeTrace::types(cause, true, expected_ty, *provided_ty); if let Some(e) = error { if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) { self.report_and_explain_type_error(trace, e).emit(); @@ -679,8 +679,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Error::Invalid(input_idx, compatibility) => { let expected_ty = expected_input_tys[input_idx]; if let Compatibility::Incompatible(error) = &compatibility { - let provided_ty = final_arg_types[input_idx].map(|ty| ty.0).unwrap(); - let cause = &self.misc(provided_args[input_idx].span); + let provided_ty = final_arg_types + .get(input_idx) + .and_then(|x| x.as_ref()) + .map(|ty| ty.0) + .unwrap_or(tcx.ty_error()); + let cause = &self.misc( + provided_args.get(input_idx).map(|i| i.span).unwrap_or(call_span), + ); let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); if let Some(e) = error { self.note_type_err( @@ -695,14 +701,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.emit_coerce_suggestions( - &mut err, - &provided_args[input_idx], - final_arg_types[input_idx].map(|ty| ty.0).unwrap(), - final_arg_types[input_idx].map(|ty| ty.1).unwrap(), - None, - None, - ); + if let Some(expr) = provided_args.get(input_idx) { + self.emit_coerce_suggestions( + &mut err, + &expr, + final_arg_types[input_idx].map(|ty| ty.0).unwrap(), + final_arg_types[input_idx].map(|ty| ty.1).unwrap(), + None, + None, + ); + } } Error::Extra(arg_idx) => { let arg_type = if let Some((_, ty)) = final_arg_types[arg_idx] { diff --git a/src/test/ui/argument-suggestions/issue-96638.rs b/src/test/ui/argument-suggestions/issue-96638.rs new file mode 100644 index 0000000000000..9c6e81ab8cc75 --- /dev/null +++ b/src/test/ui/argument-suggestions/issue-96638.rs @@ -0,0 +1,9 @@ +fn f(_: usize, _: &usize, _: usize) {} + +fn arg<T>() -> T { todo!() } + +fn main() { + let x = arg(); // `x` must be inferred + // The reference on `&x` is important to reproduce the ICE + f(&x, ""); //~ ERROR this function takes 3 arguments but 2 arguments were supplied +} diff --git a/src/test/ui/argument-suggestions/issue-96638.stderr b/src/test/ui/argument-suggestions/issue-96638.stderr new file mode 100644 index 0000000000000..35190e2ca0d82 --- /dev/null +++ b/src/test/ui/argument-suggestions/issue-96638.stderr @@ -0,0 +1,19 @@ +error[E0061]: this function takes 3 arguments but 2 arguments were supplied + --> $DIR/issue-96638.rs:8:5 + | +LL | f(&x, ""); + | ^ -- an argument of type `usize` is missing + | +note: function defined here + --> $DIR/issue-96638.rs:1:4 + | +LL | fn f(_: usize, _: &usize, _: usize) {} + | ^ -------- --------- -------- +help: provide the argument + | +LL | f({usize}, &x, {usize}); + | ~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0061`.