From 2d927cc1941cce6f320640836941ca480e958ead Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Mon, 16 Jan 2023 19:27:18 -0800 Subject: [PATCH 01/15] Explain the "no-error" io::Error case Fundamentally, querying the OS for error codes is a process that is deeply subject to the whims of chance and fortune. We can account for OS, but not for every combination of platform APIs. A compiled binary may not recognize new errors introduced years later. We should clarify a few especially odd situations, and what they mean: We can effectively promise nothing. This allows removing mention of ErrorKind::Uncategorized. That error variant is hidden quite deliberately, so we should not explicitly mention it. --- library/std/src/io/error.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 3cabf24492eaf..27aa245851617 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -359,7 +359,7 @@ pub enum ErrorKind { // "Unusual" error kinds which do not correspond simply to (sets // of) OS error codes, should be added just above this comment. - // `Other` and `Uncategorised` should remain at the end: + // `Other` and `Uncategorized` should remain at the end: // /// A custom error that does not fall under any other I/O error kind. /// @@ -565,6 +565,12 @@ impl Error { /// other standard library functions may call platform functions that may /// (or may not) reset the error value even if they succeed. /// + /// If this is used in a case where no error has yet occurred in a program, + /// e.g. right after the beginning of `fn main`, + /// then in principle any possible Error may be returned. + /// The error code may have been set by a previous program (e.g. `execve`) + /// or the OS may have initialized it to an arbitrary, even random, value. + /// /// # Examples /// /// ``` @@ -871,6 +877,13 @@ impl Error { /// Returns the corresponding [`ErrorKind`] for this error. /// + /// In some cases, the ErrorKind variant may not make much sense, + /// either because the situation does not actually involve an error, or + /// because of a new error code the standard library has not been taught. + /// See [`last_os_error`] for more details. + /// + /// [`last_os_error`]: Error::last_os_error + /// /// # Examples /// /// ``` @@ -881,7 +894,8 @@ impl Error { /// } /// /// fn main() { - /// // Will print "Uncategorized". + /// // As no error has occurred, this may print anything! + /// // It likely prints a placeholder for unidentified (non-)errors. /// print_error(Error::last_os_error()); /// // Will print "AddrInUse". /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); From e7c8af431b08dcbf01ba1fa99ffacbb7a70baf2c Mon Sep 17 00:00:00 2001 From: clubby789 Date: Tue, 28 Feb 2023 00:46:02 +0000 Subject: [PATCH 02/15] Remove issue number for `link_cfg` --- compiler/rustc_feature/src/active.rs | 4 ++-- tests/ui/feature-gates/feature-gate-link_cfg.stderr | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 6d8f7e4a0f681..1d4dd2e5c32b0 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -160,6 +160,8 @@ declare_features! ( (active, intrinsics, "1.0.0", None, None), /// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic. (active, lang_items, "1.0.0", None, None), + /// Allows `#[link(..., cfg(..))]`; perma-unstable per #37406 + (active, link_cfg, "1.14.0", None, None), /// Allows the `multiple_supertrait_upcastable` lint. (active, multiple_supertrait_upcastable, "CURRENT_RUSTC_VERSION", None, None), /// Allows using `#[omit_gdb_pretty_printer_section]`. @@ -438,8 +440,6 @@ declare_features! ( (active, large_assignments, "1.52.0", Some(83518), None), /// Allows `if/while p && let q = r && ...` chains. (active, let_chains, "1.37.0", Some(53667), None), - /// Allows `#[link(..., cfg(..))]`. - (active, link_cfg, "1.14.0", Some(37406), None), /// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check. (active, lint_reasons, "1.31.0", Some(54503), None), /// Give access to additional metadata about declarative macro meta-variables. diff --git a/tests/ui/feature-gates/feature-gate-link_cfg.stderr b/tests/ui/feature-gates/feature-gate-link_cfg.stderr index 8f47d596521d9..97b6cbca41240 100644 --- a/tests/ui/feature-gates/feature-gate-link_cfg.stderr +++ b/tests/ui/feature-gates/feature-gate-link_cfg.stderr @@ -4,7 +4,6 @@ error[E0658]: link cfg is unstable LL | #[link(name = "foo", cfg(foo))] | ^^^^^^^^ | - = note: see issue #37406 for more information = help: add `#![feature(link_cfg)]` to the crate attributes to enable error: aborting due to previous error From c6cba68c16e2dd9e2d05f3c941d58fdbebd0d1a0 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 28 Feb 2023 18:21:49 -0800 Subject: [PATCH 03/15] Add check for errant {{produces}} marker. If a lint example has an `ignore` tag, but the lint author also includes the {{produces}} marker, then the output will just contain the text `{{produces}}`. This adds a check for this mistake and provides help on how the lint docs should be written. --- src/tools/lint-docs/src/lib.rs | 38 ++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index 3842a649c6f96..e120661b96ccb 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -45,6 +45,36 @@ impl Lint { fn check_style(&self) -> Result<(), Box> { for &expected in &["### Example", "### Explanation", "{{produces}}"] { if expected == "{{produces}}" && self.is_ignored() { + if self.doc_contains("{{produces}}") { + return Err(format!( + "the lint example has `ignore`, but also contains the {{{{produces}}}} marker\n\ + \n\ + The documentation generator cannot generate the example output when the \ + example is ignored.\n\ + Manually include the sample output below the example. For example:\n\ + \n\ + /// ```rust,ignore (needs command line option)\n\ + /// #[cfg(widnows)]\n\ + /// fn foo() {{}}\n\ + /// ```\n\ + ///\n\ + /// This will produce:\n\ + /// \n\ + /// ```text\n\ + /// warning: unknown condition name used\n\ + /// --> lint_example.rs:1:7\n\ + /// |\n\ + /// 1 | #[cfg(widnows)]\n\ + /// | ^^^^^^^\n\ + /// |\n\ + /// = note: `#[warn(unexpected_cfgs)]` on by default\n\ + /// ```\n\ + \n\ + Replacing the output with the text of the example you \ + compiled manually yourself.\n\ + " + ).into()); + } continue; } if !self.doc_contains(expected) { @@ -317,10 +347,10 @@ impl<'a> LintExtractor<'a> { .., &format!( "This will produce:\n\ - \n\ - ```text\n\ - {}\ - ```", + \n\ + ```text\n\ + {}\ + ```", output ), ); From 15450b1b213d568c83bf1ba71618ddba6979e1d7 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 28 Feb 2023 18:22:05 -0800 Subject: [PATCH 04/15] Fix the ffi_unwind_calls lint documentation --- compiler/rustc_lint_defs/src/builtin.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 46ec1a2dca1f7..9090ecac9e3f4 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3967,14 +3967,9 @@ declare_lint! { /// /// ### Example /// - /// ```rust,ignore (need FFI) - /// #![feature(ffi_unwind_calls)] + /// ```rust /// #![feature(c_unwind)] - /// - /// # mod impl { - /// # #[no_mangle] - /// # pub fn "C-unwind" fn foo() {} - /// # } + /// #![warn(ffi_unwind_calls)] /// /// extern "C-unwind" { /// fn foo(); From ab2508a71f5743717a62744ee26cee11ba4d2f91 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 28 Feb 2023 18:46:54 -0800 Subject: [PATCH 05/15] Collect all matching messages for a lint. Some examples may contain multiple lines which trigger the lint. Previously it would only display the first message. This updates it so that all matching instances of the lint are displayed. --- src/tools/lint-docs/src/lib.rs | 57 +++++++++++++++++----------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index e120661b96ccb..034c6aa0708ec 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -422,37 +422,36 @@ impl<'a> LintExtractor<'a> { .filter(|line| line.starts_with('{')) .map(serde_json::from_str) .collect::, _>>()?; - match msgs + // First try to find the messages with the `code` field set to our lint. + let matches: Vec<_> = msgs .iter() - .find(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s==name)) - { - Some(msg) => { - let rendered = msg["rendered"].as_str().expect("rendered field should exist"); - Ok(rendered.to_string()) - } - None => { - match msgs.iter().find( - |msg| matches!(&msg["rendered"], serde_json::Value::String(s) if s.contains(name)), - ) { - Some(msg) => { - let rendered = msg["rendered"].as_str().expect("rendered field should exist"); - Ok(rendered.to_string()) - } - None => { - let rendered: Vec<&str> = - msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect(); - let non_json: Vec<&str> = - stderr.lines().filter(|line| !line.starts_with('{')).collect(); - Err(format!( - "did not find lint `{}` in output of example, got:\n{}\n{}", - name, - non_json.join("\n"), - rendered.join("\n") - ) - .into()) - } - } + .filter(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s==name)) + .map(|msg| msg["rendered"].as_str().expect("rendered field should exist").to_string()) + .collect(); + if matches.is_empty() { + // Some lints override their code to something else (E0566). + // Try to find something that looks like it could be our lint. + let matches: Vec<_> = msgs.iter().filter(|msg| + matches!(&msg["rendered"], serde_json::Value::String(s) if s.contains(name))) + .map(|msg| msg["rendered"].as_str().expect("rendered field should exist").to_string()) + .collect(); + if matches.is_empty() { + let rendered: Vec<&str> = + msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect(); + let non_json: Vec<&str> = + stderr.lines().filter(|line| !line.starts_with('{')).collect(); + Err(format!( + "did not find lint `{}` in output of example, got:\n{}\n{}", + name, + non_json.join("\n"), + rendered.join("\n") + ) + .into()) + } else { + Ok(matches.join("\n")) } + } else { + Ok(matches.join("\n")) } } From 4f7cd3d4591aefc4edec1039ac49bef94d65deb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 16 Mar 2023 21:42:31 +0100 Subject: [PATCH 06/15] Add `try_canonicalize` to `rustc_fs_util` and use it over `fs::canonicalize` --- Cargo.lock | 3 +++ compiler/rustc_fs_util/src/lib.rs | 8 +++++++- compiler/rustc_incremental/src/persist/fs.rs | 8 ++++---- compiler/rustc_interface/Cargo.toml | 1 + compiler/rustc_interface/src/passes.rs | 5 +++-- compiler/rustc_metadata/Cargo.toml | 1 + compiler/rustc_metadata/src/locator.rs | 7 ++++--- compiler/rustc_session/src/filesearch.rs | 5 +++-- compiler/rustc_session/src/utils.rs | 3 ++- compiler/rustc_target/Cargo.toml | 1 + compiler/rustc_target/src/spec/mod.rs | 3 ++- 11 files changed, 31 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 49b89c71994a8..2394aa6e4531f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4826,6 +4826,7 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_expand", + "rustc_fs_util", "rustc_hir", "rustc_hir_analysis", "rustc_hir_typeck", @@ -4950,6 +4951,7 @@ dependencies = [ "rustc_errors", "rustc_expand", "rustc_feature", + "rustc_fs_util", "rustc_hir", "rustc_hir_pretty", "rustc_index", @@ -5334,6 +5336,7 @@ dependencies = [ "rustc_abi", "rustc_data_structures", "rustc_feature", + "rustc_fs_util", "rustc_index", "rustc_macros", "rustc_serialize", diff --git a/compiler/rustc_fs_util/src/lib.rs b/compiler/rustc_fs_util/src/lib.rs index a7dfce3b9b8fc..81d6333814544 100644 --- a/compiler/rustc_fs_util/src/lib.rs +++ b/compiler/rustc_fs_util/src/lib.rs @@ -1,10 +1,11 @@ +#![feature(absolute_path)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] use std::ffi::CString; use std::fs; use std::io; -use std::path::{Path, PathBuf}; +use std::path::{absolute, Path, PathBuf}; // Unfortunately, on windows, it looks like msvcrt.dll is silently translating // verbatim paths under the hood to non-verbatim paths! This manifests itself as @@ -91,3 +92,8 @@ pub fn path_to_c_string(p: &Path) -> CString { pub fn path_to_c_string(p: &Path) -> CString { CString::new(p.to_str().unwrap()).unwrap() } + +#[inline] +pub fn try_canonicalize>(path: P) -> io::Result { + fs::canonicalize(&path).or_else(|_| absolute(&path)) +} diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 4deae9f41c712..d6f83838a0417 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -108,7 +108,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::svh::Svh; use rustc_data_structures::{base_n, flock}; use rustc_errors::ErrorGuaranteed; -use rustc_fs_util::{link_or_copy, LinkOrCopy}; +use rustc_fs_util::{link_or_copy, try_canonicalize, LinkOrCopy}; use rustc_session::{Session, StableCrateId}; use rustc_span::Symbol; @@ -223,7 +223,7 @@ pub fn prepare_session_directory( // because, on windows, long paths can cause problems; // canonicalization inserts this weird prefix that makes windows // tolerate long paths. - let crate_dir = match crate_dir.canonicalize() { + let crate_dir = match try_canonicalize(&crate_dir) { Ok(v) => v, Err(err) => { return Err(sess.emit_err(errors::CanonicalizePath { path: crate_dir, err })); @@ -867,7 +867,7 @@ fn all_except_most_recent( /// before passing it to std::fs::remove_dir_all(). This will convert the path /// into the '\\?\' format, which supports much longer paths. fn safe_remove_dir_all(p: &Path) -> io::Result<()> { - let canonicalized = match std_fs::canonicalize(p) { + let canonicalized = match try_canonicalize(p) { Ok(canonicalized) => canonicalized, Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()), Err(err) => return Err(err), @@ -877,7 +877,7 @@ fn safe_remove_dir_all(p: &Path) -> io::Result<()> { } fn safe_remove_file(p: &Path) -> io::Result<()> { - let canonicalized = match std_fs::canonicalize(p) { + let canonicalized = match try_canonicalize(p) { Ok(canonicalized) => canonicalized, Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()), Err(err) => return Err(err), diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index ac6e8fca69554..96d6a1cb062ee 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -16,6 +16,7 @@ rustc_attr = { path = "../rustc_attr" } rustc_borrowck = { path = "../rustc_borrowck" } rustc_builtin_macros = { path = "../rustc_builtin_macros" } rustc_expand = { path = "../rustc_expand" } +rustc_fs_util = { path = "../rustc_fs_util" } rustc_macros = { path = "../rustc_macros" } rustc_parse = { path = "../rustc_parse" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 71bdd4df95ba3..b392624a95362 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -12,6 +12,7 @@ use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; use rustc_errors::PResult; use rustc_expand::base::{ExtCtxt, LintStoreExpand}; +use rustc_fs_util::try_canonicalize; use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE}; use rustc_lint::{unerased_lint_store, BufferedEarlyLint, EarlyCheckNode, LintStore}; use rustc_metadata::creader::CStore; @@ -405,12 +406,12 @@ where } fn output_contains_path(output_paths: &[PathBuf], input_path: &Path) -> bool { - let input_path = input_path.canonicalize().ok(); + let input_path = try_canonicalize(input_path).ok(); if input_path.is_none() { return false; } let check = |output_path: &PathBuf| { - if output_path.canonicalize().ok() == input_path { Some(()) } else { None } + if try_canonicalize(output_path).ok() == input_path { Some(()) } else { None } }; check_output(output_paths, check).is_some() } diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index bee5c8541d68e..4d7c133e09bc4 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -18,6 +18,7 @@ rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } +rustc_fs_util = { path = "../rustc_fs_util" } rustc_hir = { path = "../rustc_hir" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index c48e681eb94ad..79c42a128e795 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -222,6 +222,7 @@ use rustc_data_structures::owning_ref::OwningRef; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::MetadataRef; use rustc_errors::{DiagnosticArgValue, FatalError, IntoDiagnosticArg}; +use rustc_fs_util::try_canonicalize; use rustc_session::config::{self, CrateType}; use rustc_session::cstore::{CrateSource, MetadataLoader}; use rustc_session::filesearch::FileSearch; @@ -236,7 +237,7 @@ use snap::read::FrameDecoder; use std::borrow::Cow; use std::io::{Read, Result as IoResult, Write}; use std::path::{Path, PathBuf}; -use std::{cmp, fmt, fs}; +use std::{cmp, fmt}; #[derive(Clone)] pub(crate) struct CrateLocator<'a> { @@ -441,7 +442,7 @@ impl<'a> CrateLocator<'a> { info!("lib candidate: {}", spf.path.display()); let (rlibs, rmetas, dylibs) = candidates.entry(hash.to_string()).or_default(); - let path = fs::canonicalize(&spf.path).unwrap_or_else(|_| spf.path.clone()); + let path = try_canonicalize(&spf.path).unwrap_or_else(|_| spf.path.clone()); if seen_paths.contains(&path) { continue; }; @@ -636,7 +637,7 @@ impl<'a> CrateLocator<'a> { // as well. if let Some((prev, _)) = &ret { let sysroot = self.sysroot; - let sysroot = sysroot.canonicalize().unwrap_or_else(|_| sysroot.to_path_buf()); + let sysroot = try_canonicalize(sysroot).unwrap_or_else(|_| sysroot.to_path_buf()); if prev.starts_with(&sysroot) { continue; } diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index f1fbf38217d64..6ac05c3f122bc 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -1,5 +1,6 @@ //! A module for searching for libraries +use rustc_fs_util::try_canonicalize; use smallvec::{smallvec, SmallVec}; use std::env; use std::fs; @@ -122,7 +123,7 @@ pub fn sysroot_candidates() -> SmallVec<[PathBuf; 2]> { let target = crate::config::host_triple(); let mut sysroot_candidates: SmallVec<[PathBuf; 2]> = smallvec![get_or_default_sysroot().expect("Failed finding sysroot")]; - let path = current_dll_path().and_then(|s| s.canonicalize().map_err(|e| e.to_string())); + let path = current_dll_path().and_then(|s| try_canonicalize(s).map_err(|e| e.to_string())); if let Ok(dll) = path { // use `parent` twice to chop off the file name and then also the // directory containing the dll which should be either `lib` or `bin`. @@ -157,7 +158,7 @@ pub fn sysroot_candidates() -> SmallVec<[PathBuf; 2]> { pub fn get_or_default_sysroot() -> Result { // Follow symlinks. If the resolved path is relative, make it absolute. fn canonicalize(path: PathBuf) -> PathBuf { - let path = fs::canonicalize(&path).unwrap_or(path); + let path = try_canonicalize(&path).unwrap_or(path); // See comments on this target function, but the gist is that // gcc chokes on verbatim paths which fs::canonicalize generates // so we try to avoid those kinds of paths. diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index b996d36a318c5..47fe9a990d36a 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -1,5 +1,6 @@ use crate::session::Session; use rustc_data_structures::profiling::VerboseTimingGuard; +use rustc_fs_util::try_canonicalize; use std::path::{Path, PathBuf}; impl Session { @@ -91,7 +92,7 @@ pub struct CanonicalizedPath { impl CanonicalizedPath { pub fn new(path: &Path) -> Self { - Self { original: path.to_owned(), canonicalized: std::fs::canonicalize(path).ok() } + Self { original: path.to_owned(), canonicalized: try_canonicalize(path).ok() } } pub fn canonicalized(&self) -> &PathBuf { diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml index 568c916a16328..4e7a8d166ae69 100644 --- a/compiler/rustc_target/Cargo.toml +++ b/compiler/rustc_target/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" bitflags = "1.2.1" tracing = "0.1" serde_json = "1.0.59" +rustc_fs_util = { path = "../rustc_fs_util" } rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f3304e9142920..2553b11d8789b 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -40,6 +40,7 @@ use crate::json::{Json, ToJson}; use crate::spec::abi::{lookup as lookup_abi, Abi}; use crate::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_fs_util::try_canonicalize; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::symbol::{sym, Symbol}; use serde_json::Value; @@ -2949,7 +2950,7 @@ impl TargetTriple { /// Creates a target triple from the passed target path. pub fn from_path(path: &Path) -> Result { - let canonicalized_path = path.canonicalize()?; + let canonicalized_path = try_canonicalize(path)?; let contents = std::fs::read_to_string(&canonicalized_path).map_err(|err| { io::Error::new( io::ErrorKind::InvalidInput, From 0f32fd8484d3282648dc18985b7d29abad2d6b70 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Fri, 17 Mar 2023 14:24:11 -0700 Subject: [PATCH 07/15] Remove irrelevant docs on error kinds --- library/std/src/io/error.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 27aa245851617..6dd7859fcb40b 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -565,12 +565,6 @@ impl Error { /// other standard library functions may call platform functions that may /// (or may not) reset the error value even if they succeed. /// - /// If this is used in a case where no error has yet occurred in a program, - /// e.g. right after the beginning of `fn main`, - /// then in principle any possible Error may be returned. - /// The error code may have been set by a previous program (e.g. `execve`) - /// or the OS may have initialized it to an arbitrary, even random, value. - /// /// # Examples /// /// ``` @@ -877,9 +871,9 @@ impl Error { /// Returns the corresponding [`ErrorKind`] for this error. /// - /// In some cases, the ErrorKind variant may not make much sense, - /// either because the situation does not actually involve an error, or - /// because of a new error code the standard library has not been taught. + /// This may be a value set by Rust code constructing custom `io::Error`s, + /// or if this `io::Error` was sourced from the operating system, + /// it will be a value inferred from the system's error encoding. /// See [`last_os_error`] for more details. /// /// [`last_os_error`]: Error::last_os_error @@ -894,7 +888,7 @@ impl Error { /// } /// /// fn main() { - /// // As no error has occurred, this may print anything! + /// // As no error has (visibly) occurred, this may print anything! /// // It likely prints a placeholder for unidentified (non-)errors. /// print_error(Error::last_os_error()); /// // Will print "AddrInUse". From 1f67949f0e9cd6c80a3e325164ef17663127f07b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Mon, 20 Mar 2023 03:11:28 +0800 Subject: [PATCH 08/15] Lint ambiguous glob re-exports --- compiler/rustc_lint/src/context.rs | 4 ++ compiler/rustc_lint_defs/src/builtin.rs | 40 +++++++++++ compiler/rustc_lint_defs/src/lib.rs | 10 +++ .../src/effective_visibilities.rs | 68 ++++++++++++++----- compiler/rustc_resolve/src/imports.rs | 32 ++++++++- compiler/rustc_resolve/src/lib.rs | 5 +- tests/ui/imports/auxiliary/glob-conflict.rs | 2 + .../local-modularized-tricky-fail-1.rs | 1 + .../local-modularized-tricky-fail-1.stderr | 14 ++-- .../issue-107563-ambiguous-glob-reexports.rs | 33 +++++++++ ...sue-107563-ambiguous-glob-reexports.stderr | 47 +++++++++++++ 11 files changed, 229 insertions(+), 27 deletions(-) create mode 100644 tests/ui/resolve/issue-107563-ambiguous-glob-reexports.rs create mode 100644 tests/ui/resolve/issue-107563-ambiguous-glob-reexports.stderr diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index f5a711315ea4c..626c09fea07ac 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -910,6 +910,10 @@ pub trait LintContext: Sized { Applicability::MachineApplicable, ); } + BuiltinLintDiagnostics::AmbiguousGlobReexports { name, namespace, first_reexport_span, duplicate_reexport_span } => { + db.span_label(first_reexport_span, format!("the name `{}` in the {} namespace is first re-exported here", name, namespace)); + db.span_label(duplicate_reexport_span, format!("but the name `{}` in the {} namespace is also re-exported here", name, namespace)); + } } // Rewrap `db`, and pass control to the user. decorate(db) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 91966e75b5fa2..242d147b5ee11 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3230,6 +3230,45 @@ declare_lint! { }; } +declare_lint! { + /// The `ambiguous_glob_reexports` lint detects cases where names re-exported via globs + /// collide. Downstream users trying to use the same name re-exported from multiple globs + /// will receive a warning pointing out redefinition of the same name. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(ambiguous_glob_reexports)] + /// pub mod foo { + /// pub type X = u8; + /// } + /// + /// pub mod bar { + /// pub type Y = u8; + /// pub type X = u8; + /// } + /// + /// pub use foo::*; + /// pub use bar::*; + /// + /// + /// pub fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This was previously accepted but it could silently break a crate's downstream users code. + /// For example, if `foo::*` and `bar::*` were re-exported before `bar::X` was added to the + /// re-exports, down stream users could use `this_crate::X` without problems. However, adding + /// `bar::X` would cause compilation errors in downstream crates because `X` is defined + /// multiple times in the same namespace of `this_crate`. + pub AMBIGUOUS_GLOB_REEXPORTS, + Warn, + "ambiguous glob re-exports", +} + declare_lint_pass! { /// Does nothing as a lint pass, but registers some `Lint`s /// that are used by other parts of the compiler. @@ -3337,6 +3376,7 @@ declare_lint_pass! { NAMED_ARGUMENTS_USED_POSITIONALLY, IMPLIED_BOUNDS_ENTAILMENT, BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE, + AMBIGUOUS_GLOB_REEXPORTS, ] } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 6f22bdabff450..69a8b691ab216 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -529,6 +529,16 @@ pub enum BuiltinLintDiagnostics { vis_span: Span, ident_span: Span, }, + AmbiguousGlobReexports { + /// The name for which collision(s) have occurred. + name: String, + /// The name space for whihc the collision(s) occurred in. + namespace: String, + /// Span where the name is first re-exported. + first_reexport_span: Span, + /// Span where the same name is also re-exported. + duplicate_reexport_span: Span, + }, } /// Lints that are buffered up early on in the `Session` before the diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index 4bb252bfb29dd..a1ae9b8a52181 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -4,6 +4,7 @@ use rustc_ast::visit; use rustc_ast::visit::Visitor; use rustc_ast::Crate; use rustc_ast::EnumDef; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::intern::Interned; use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::CRATE_DEF_ID; @@ -70,11 +71,11 @@ impl Resolver<'_, '_> { impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> { /// Fills the `Resolver::effective_visibilities` table with public & exported items /// For now, this doesn't resolve macros (FIXME) and cannot resolve Impl, as we - /// need access to a TyCtxt for that. + /// need access to a TyCtxt for that. Returns the set of ambiguous re-exports. pub(crate) fn compute_effective_visibilities<'c>( r: &'r mut Resolver<'a, 'tcx>, krate: &'c Crate, - ) { + ) -> FxHashSet>> { let mut visitor = EffectiveVisibilitiesVisitor { r, def_effective_visibilities: Default::default(), @@ -93,18 +94,26 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> { } visitor.r.effective_visibilities = visitor.def_effective_visibilities; + let mut exported_ambiguities = FxHashSet::default(); + // Update visibilities for import def ids. These are not used during the // `EffectiveVisibilitiesVisitor` pass, because we have more detailed binding-based // information, but are used by later passes. Effective visibility of an import def id // is the maximum value among visibilities of bindings corresponding to that def id. for (binding, eff_vis) in visitor.import_effective_visibilities.iter() { let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() }; - if let Some(node_id) = import.id() { - r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx) + if !binding.is_ambiguity() { + if let Some(node_id) = import.id() { + r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx) + } + } else if binding.ambiguity.is_some() && eff_vis.is_public_at_level(Level::Reexported) { + exported_ambiguities.insert(*binding); } } info!("resolve::effective_visibilities: {:#?}", r.effective_visibilities); + + exported_ambiguities } /// Update effective visibilities of bindings in the given module, @@ -115,21 +124,44 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> { let resolutions = self.r.resolutions(module); for (_, name_resolution) in resolutions.borrow().iter() { - if let Some(mut binding) = name_resolution.borrow().binding() && !binding.is_ambiguity() { - // Set the given effective visibility level to `Level::Direct` and - // sets the rest of the `use` chain to `Level::Reexported` until - // we hit the actual exported item. - let mut parent_id = ParentId::Def(module_id); - while let NameBindingKind::Import { binding: nested_binding, .. } = binding.kind { - let binding_id = ImportId::new_unchecked(binding); - self.update_import(binding_id, parent_id); - - parent_id = ParentId::Import(binding_id); - binding = nested_binding; - } + if let Some(mut binding) = name_resolution.borrow().binding() { + if !binding.is_ambiguity() { + // Set the given effective visibility level to `Level::Direct` and + // sets the rest of the `use` chain to `Level::Reexported` until + // we hit the actual exported item. + let mut parent_id = ParentId::Def(module_id); + while let NameBindingKind::Import { binding: nested_binding, .. } = binding.kind + { + let binding_id = ImportId::new_unchecked(binding); + self.update_import(binding_id, parent_id); + + parent_id = ParentId::Import(binding_id); + binding = nested_binding; + } + + if let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) { + self.update_def(def_id, binding.vis.expect_local(), parent_id); + } + } else { + // Put the root ambiguity binding and all reexports leading to it into the + // table. They are used by the `ambiguous_glob_reexports` lint. For all + // bindings added to the table here `is_ambiguity` returns true. + let mut parent_id = ParentId::Def(module_id); + while let NameBindingKind::Import { binding: nested_binding, .. } = binding.kind + { + let binding_id = ImportId::new_unchecked(binding); + self.update_import(binding_id, parent_id); - if let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) { - self.update_def(def_id, binding.vis.expect_local(), parent_id); + if binding.ambiguity.is_some() { + // Stop at the root ambiguity, further bindings in the chain should not + // be reexported because the root ambiguity blocks any access to them. + // (Those further bindings are most likely not ambiguities themselves.) + break; + } + + parent_id = ParentId::Import(binding_id); + binding = nested_binding; + } } } } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 6e27bcc5bf3d1..b36fca9bb9623 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -19,7 +19,9 @@ use rustc_hir::def::{self, DefKind, PartialRes}; use rustc_middle::metadata::ModChild; use rustc_middle::span_bug; use rustc_middle::ty; -use rustc_session::lint::builtin::{PUB_USE_OF_PRIVATE_EXTERN_CRATE, UNUSED_IMPORTS}; +use rustc_session::lint::builtin::{ + AMBIGUOUS_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE, UNUSED_IMPORTS, +}; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::LocalExpnId; @@ -506,6 +508,34 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } + pub(crate) fn check_reexport_ambiguities( + &mut self, + exported_ambiguities: FxHashSet>>, + ) { + for module in self.arenas.local_modules().iter() { + module.for_each_child(self, |this, ident, ns, binding| { + if let NameBindingKind::Import { import, .. } = binding.kind + && let Some((amb_binding, _)) = binding.ambiguity + && binding.res() != Res::Err + && exported_ambiguities.contains(&Interned::new_unchecked(binding)) + { + this.lint_buffer.buffer_lint_with_diagnostic( + AMBIGUOUS_GLOB_REEXPORTS, + import.root_id, + import.root_span, + "ambiguous glob re-exports", + BuiltinLintDiagnostics::AmbiguousGlobReexports { + name: ident.to_string(), + namespace: ns.descr().to_string(), + first_reexport_span: import.root_span, + duplicate_reexport_span: amb_binding.span, + }, + ); + } + }); + } + } + fn throw_unresolved_import_error(&self, errors: Vec<(&Import<'_>, UnresolvedImportError)>) { if errors.is_empty() { return; diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index cd90fd3ef84d8..de603d87e6d2d 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1474,9 +1474,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { pub fn resolve_crate(&mut self, krate: &Crate) { self.tcx.sess.time("resolve_crate", || { self.tcx.sess.time("finalize_imports", || self.finalize_imports()); - self.tcx.sess.time("compute_effective_visibilities", || { + let exported_ambiguities = self.tcx.sess.time("compute_effective_visibilities", || { EffectiveVisibilitiesVisitor::compute_effective_visibilities(self, krate) }); + self.tcx.sess.time("check_reexport_ambiguities", || { + self.check_reexport_ambiguities(exported_ambiguities) + }); self.tcx.sess.time("finalize_macro_resolutions", || self.finalize_macro_resolutions()); self.tcx.sess.time("late_resolve_crate", || self.late_resolve_crate(krate)); self.tcx.sess.time("resolve_main", || self.resolve_main()); diff --git a/tests/ui/imports/auxiliary/glob-conflict.rs b/tests/ui/imports/auxiliary/glob-conflict.rs index c83db64c6437c..8a146378b4395 100644 --- a/tests/ui/imports/auxiliary/glob-conflict.rs +++ b/tests/ui/imports/auxiliary/glob-conflict.rs @@ -1,3 +1,5 @@ +#![allow(ambiguous_glob_reexports)] + mod m1 { pub fn f() {} } diff --git a/tests/ui/imports/local-modularized-tricky-fail-1.rs b/tests/ui/imports/local-modularized-tricky-fail-1.rs index 29e9b8ec841f5..ce700ae0de9b1 100644 --- a/tests/ui/imports/local-modularized-tricky-fail-1.rs +++ b/tests/ui/imports/local-modularized-tricky-fail-1.rs @@ -1,4 +1,5 @@ #![feature(decl_macro)] +#![allow(ambiguous_glob_reexports)] macro_rules! define_exported { () => { #[macro_export] diff --git a/tests/ui/imports/local-modularized-tricky-fail-1.stderr b/tests/ui/imports/local-modularized-tricky-fail-1.stderr index 20eadaaaa56b8..52a01e8bcdfe3 100644 --- a/tests/ui/imports/local-modularized-tricky-fail-1.stderr +++ b/tests/ui/imports/local-modularized-tricky-fail-1.stderr @@ -1,12 +1,12 @@ error[E0659]: `exported` is ambiguous - --> $DIR/local-modularized-tricky-fail-1.rs:28:1 + --> $DIR/local-modularized-tricky-fail-1.rs:29:1 | LL | exported!(); | ^^^^^^^^ ambiguous name | = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution note: `exported` could refer to the macro defined here - --> $DIR/local-modularized-tricky-fail-1.rs:5:5 + --> $DIR/local-modularized-tricky-fail-1.rs:6:5 | LL | / macro_rules! exported { LL | | () => () @@ -16,7 +16,7 @@ LL | | } LL | define_exported!(); | ------------------ in this macro invocation note: `exported` could also refer to the macro imported here - --> $DIR/local-modularized-tricky-fail-1.rs:22:5 + --> $DIR/local-modularized-tricky-fail-1.rs:23:5 | LL | use inner1::*; | ^^^^^^^^^ @@ -24,7 +24,7 @@ LL | use inner1::*; = note: this error originates in the macro `define_exported` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0659]: `panic` is ambiguous - --> $DIR/local-modularized-tricky-fail-1.rs:35:5 + --> $DIR/local-modularized-tricky-fail-1.rs:36:5 | LL | panic!(); | ^^^^^ ambiguous name @@ -32,7 +32,7 @@ LL | panic!(); = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution = note: `panic` could refer to a macro from prelude note: `panic` could also refer to the macro defined here - --> $DIR/local-modularized-tricky-fail-1.rs:11:5 + --> $DIR/local-modularized-tricky-fail-1.rs:12:5 | LL | / macro_rules! panic { LL | | () => () @@ -45,7 +45,7 @@ LL | define_panic!(); = note: this error originates in the macro `define_panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0659]: `include` is ambiguous - --> $DIR/local-modularized-tricky-fail-1.rs:46:1 + --> $DIR/local-modularized-tricky-fail-1.rs:47:1 | LL | include!(); | ^^^^^^^ ambiguous name @@ -53,7 +53,7 @@ LL | include!(); = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution = note: `include` could refer to a macro from prelude note: `include` could also refer to the macro defined here - --> $DIR/local-modularized-tricky-fail-1.rs:17:5 + --> $DIR/local-modularized-tricky-fail-1.rs:18:5 | LL | / macro_rules! include { LL | | () => () diff --git a/tests/ui/resolve/issue-107563-ambiguous-glob-reexports.rs b/tests/ui/resolve/issue-107563-ambiguous-glob-reexports.rs new file mode 100644 index 0000000000000..431213e25e462 --- /dev/null +++ b/tests/ui/resolve/issue-107563-ambiguous-glob-reexports.rs @@ -0,0 +1,33 @@ +#![deny(ambiguous_glob_reexports)] + +pub mod foo { + pub type X = u8; +} + +pub mod bar { + pub type X = u8; + pub type Y = u8; +} + +pub use foo::*; +//~^ ERROR ambiguous glob re-exports +pub use bar::*; + +mod ambiguous { + mod m1 { pub type A = u8; } + mod m2 { pub type A = u8; } + pub use self::m1::*; + //~^ ERROR ambiguous glob re-exports + pub use self::m2::*; +} + +pub mod single { + pub use ambiguous::A; + //~^ ERROR `A` is ambiguous +} + +pub mod glob { + pub use ambiguous::*; +} + +pub fn main() {} diff --git a/tests/ui/resolve/issue-107563-ambiguous-glob-reexports.stderr b/tests/ui/resolve/issue-107563-ambiguous-glob-reexports.stderr new file mode 100644 index 0000000000000..07e61dd8643d4 --- /dev/null +++ b/tests/ui/resolve/issue-107563-ambiguous-glob-reexports.stderr @@ -0,0 +1,47 @@ +error[E0659]: `A` is ambiguous + --> $DIR/issue-107563-ambiguous-glob-reexports.rs:25:24 + | +LL | pub use ambiguous::A; + | ^ ambiguous name + | + = note: ambiguous because of multiple glob imports of a name in the same module +note: `A` could refer to the type alias imported here + --> $DIR/issue-107563-ambiguous-glob-reexports.rs:19:13 + | +LL | pub use self::m1::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `A` to disambiguate +note: `A` could also refer to the type alias imported here + --> $DIR/issue-107563-ambiguous-glob-reexports.rs:21:13 + | +LL | pub use self::m2::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `A` to disambiguate + +error: ambiguous glob re-exports + --> $DIR/issue-107563-ambiguous-glob-reexports.rs:12:9 + | +LL | pub use foo::*; + | ^^^^^^ the name `X` in the type namespace is first re-exported here +LL | +LL | pub use bar::*; + | ------ but the name `X` in the type namespace is also re-exported here + | +note: the lint level is defined here + --> $DIR/issue-107563-ambiguous-glob-reexports.rs:1:9 + | +LL | #![deny(ambiguous_glob_reexports)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ambiguous glob re-exports + --> $DIR/issue-107563-ambiguous-glob-reexports.rs:19:13 + | +LL | pub use self::m1::*; + | ^^^^^^^^^^^ the name `A` in the type namespace is first re-exported here +LL | +LL | pub use self::m2::*; + | ----------- but the name `A` in the type namespace is also re-exported here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0659`. From b0dc15c61ba38ad86aa9d565f99189db4a91f4c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Mon, 6 Feb 2023 08:31:19 +0100 Subject: [PATCH 09/15] Reduce output spam --- compiler/rustc_codegen_llvm/src/lib.rs | 8 ++++---- compiler/rustc_hir_analysis/src/coherence/mod.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 8dafe1b750b33..e5bae009ed64b 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -361,12 +361,12 @@ impl CodegenBackend for LlvmCodegenBackend { .expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box") .join(sess); - sess.time("llvm_dump_timing_file", || { - if sess.opts.unstable_opts.llvm_time_trace { + if sess.opts.unstable_opts.llvm_time_trace { + sess.time("llvm_dump_timing_file", || { let file_name = outputs.with_extension("llvm_timings.json"); llvm_util::time_trace_profiler_finish(&file_name); - } - }); + }); + } Ok((codegen_results, work_products)) } diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 23490bc091c1c..465e787c92ae6 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -133,8 +133,8 @@ fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) { check_impl(tcx, impl_def_id, trait_ref); check_object_overlap(tcx, impl_def_id, trait_ref); - tcx.sess.time("unsafety_checking", || unsafety::check_item(tcx, impl_def_id)); - tcx.sess.time("orphan_checking", || tcx.ensure().orphan_check_impl(impl_def_id)); + unsafety::check_item(tcx, impl_def_id); + tcx.ensure().orphan_check_impl(impl_def_id); } builtin::check_trait(tcx, def_id); From f60d2eb6c19751154a2c752d1c916420c0bb60e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Mon, 6 Feb 2023 06:32:34 +0100 Subject: [PATCH 10/15] Add `-Z time-passes-format` to allow specifying a JSON output for `-Z time-passes` --- Cargo.lock | 1 + compiler/rustc_codegen_ssa/src/base.rs | 2 + compiler/rustc_data_structures/Cargo.toml | 1 + .../src/graph/scc/tests.rs | 6 +- .../src/graph/vec_graph/tests.rs | 6 +- .../rustc_data_structures/src/profiling.rs | 72 +++++++++++++++---- compiler/rustc_driver_impl/src/lib.rs | 13 ++-- compiler/rustc_interface/src/tests.rs | 2 + compiler/rustc_session/src/options.rs | 19 +++++ compiler/rustc_session/src/session.rs | 5 +- compiler/rustc_session/src/utils.rs | 4 +- tests/rustdoc-ui/z-help.stdout | 1 + 12 files changed, 105 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61f2cfb9e22c4..e5eae1a83c14e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4523,6 +4523,7 @@ dependencies = [ "rustc_index", "rustc_macros", "rustc_serialize", + "serde_json", "smallvec", "stable_deref_trait", "stacker", diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index abc510e360d56..a109633520ab9 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -786,6 +786,8 @@ pub fn codegen_crate( total_codegen_time, start_rss.unwrap(), end_rss, + tcx.sess.opts.unstable_opts.time_passes_format, + true, ); } diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 24cf9812a25a1..056ee1f63be03 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -21,6 +21,7 @@ rustc-hash = "1.1.0" rustc_index = { path = "../rustc_index", package = "rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } +serde_json = "1.0.59" smallvec = { version = "1.8.1", features = [ "const_generics", "union", diff --git a/compiler/rustc_data_structures/src/graph/scc/tests.rs b/compiler/rustc_data_structures/src/graph/scc/tests.rs index 820a70fc8e446..513df666d0da1 100644 --- a/compiler/rustc_data_structures/src/graph/scc/tests.rs +++ b/compiler/rustc_data_structures/src/graph/scc/tests.rs @@ -56,7 +56,7 @@ fn test_three_sccs() { assert_eq!(sccs.scc(1), 0); assert_eq!(sccs.scc(2), 0); assert_eq!(sccs.scc(3), 2); - assert_eq!(sccs.successors(0), &[]); + assert_eq!(sccs.successors(0), &[] as &[usize]); assert_eq!(sccs.successors(1), &[0]); assert_eq!(sccs.successors(2), &[0]); } @@ -113,7 +113,7 @@ fn test_find_state_2() { assert_eq!(sccs.scc(2), 0); assert_eq!(sccs.scc(3), 0); assert_eq!(sccs.scc(4), 0); - assert_eq!(sccs.successors(0), &[]); + assert_eq!(sccs.successors(0), &[] as &[usize]); } #[test] @@ -138,7 +138,7 @@ fn test_find_state_3() { assert_eq!(sccs.scc(3), 0); assert_eq!(sccs.scc(4), 0); assert_eq!(sccs.scc(5), 1); - assert_eq!(sccs.successors(0), &[]); + assert_eq!(sccs.successors(0), &[] as &[usize]); assert_eq!(sccs.successors(1), &[0]); } diff --git a/compiler/rustc_data_structures/src/graph/vec_graph/tests.rs b/compiler/rustc_data_structures/src/graph/vec_graph/tests.rs index c8f979267170f..7c866da60090f 100644 --- a/compiler/rustc_data_structures/src/graph/vec_graph/tests.rs +++ b/compiler/rustc_data_structures/src/graph/vec_graph/tests.rs @@ -27,11 +27,11 @@ fn successors() { let graph = create_graph(); assert_eq!(graph.successors(0), &[1]); assert_eq!(graph.successors(1), &[2, 3]); - assert_eq!(graph.successors(2), &[]); + assert_eq!(graph.successors(2), &[] as &[usize]); assert_eq!(graph.successors(3), &[4]); - assert_eq!(graph.successors(4), &[]); + assert_eq!(graph.successors(4), &[] as &[usize]); assert_eq!(graph.successors(5), &[1]); - assert_eq!(graph.successors(6), &[]); + assert_eq!(graph.successors(6), &[] as &[usize]); } #[test] diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 3d9c7f6eae276..87b74903d5125 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -97,6 +97,7 @@ use std::time::{Duration, Instant}; pub use measureme::EventId; use measureme::{EventIdBuilder, Profiler, SerializableString, StringId}; use parking_lot::RwLock; +use serde_json::json; use smallvec::SmallVec; bitflags::bitflags! { @@ -145,6 +146,15 @@ const EVENT_FILTERS_BY_NAME: &[(&str, EventFilter)] = &[ /// Something that uniquely identifies a query invocation. pub struct QueryInvocationId(pub u32); +/// Which format to use for `-Z time-passes` +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum TimePassesFormat { + /// Emit human readable text + Text, + /// Emit structured JSON + Json, +} + /// A reference to the SelfProfiler. It can be cloned and sent across thread /// boundaries at will. #[derive(Clone)] @@ -158,14 +168,14 @@ pub struct SelfProfilerRef { // actually enabled. event_filter_mask: EventFilter, - // Print verbose generic activities to stderr? - print_verbose_generic_activities: bool, + // Print verbose generic activities to stderr. + print_verbose_generic_activities: Option, } impl SelfProfilerRef { pub fn new( profiler: Option>, - print_verbose_generic_activities: bool, + print_verbose_generic_activities: Option, ) -> SelfProfilerRef { // If there is no SelfProfiler then the filter mask is set to NONE, // ensuring that nothing ever tries to actually access it. @@ -207,9 +217,10 @@ impl SelfProfilerRef { /// a measureme event, "verbose" generic activities also print a timing entry to /// stderr if the compiler is invoked with -Ztime-passes. pub fn verbose_generic_activity(&self, event_label: &'static str) -> VerboseTimingGuard<'_> { - let message = self.print_verbose_generic_activities.then(|| event_label.to_owned()); + let message_and_format = + self.print_verbose_generic_activities.map(|format| (event_label.to_owned(), format)); - VerboseTimingGuard::start(message, self.generic_activity(event_label)) + VerboseTimingGuard::start(message_and_format, self.generic_activity(event_label), false) } /// Like `verbose_generic_activity`, but with an extra arg. @@ -221,11 +232,26 @@ impl SelfProfilerRef { where A: Borrow + Into, { - let message = self + let message_and_format = self .print_verbose_generic_activities - .then(|| format!("{}({})", event_label, event_arg.borrow())); + .map(|format| (format!("{}({})", event_label, event_arg.borrow()), format)); - VerboseTimingGuard::start(message, self.generic_activity_with_arg(event_label, event_arg)) + VerboseTimingGuard::start( + message_and_format, + self.generic_activity_with_arg(event_label, event_arg), + false, + ) + } + + /// Like `verbose_generic_activity`, but `event_label` must be unique for a rustc session. + pub fn unique_verbose_generic_activity( + &self, + event_label: &'static str, + ) -> VerboseTimingGuard<'_> { + let message_and_format = + self.print_verbose_generic_activities.map(|format| (event_label.to_owned(), format)); + + VerboseTimingGuard::start(message_and_format, self.generic_activity(event_label), true) } /// Start profiling a generic activity. Profiling continues until the @@ -705,15 +731,21 @@ impl<'a> TimingGuard<'a> { #[must_use] pub struct VerboseTimingGuard<'a> { - start_and_message: Option<(Instant, Option, String)>, + start_and_message: Option<(Instant, Option, String, TimePassesFormat, bool)>, _guard: TimingGuard<'a>, } impl<'a> VerboseTimingGuard<'a> { - pub fn start(message: Option, _guard: TimingGuard<'a>) -> Self { + pub fn start( + message_and_format: Option<(String, TimePassesFormat)>, + _guard: TimingGuard<'a>, + unique: bool, + ) -> Self { VerboseTimingGuard { _guard, - start_and_message: message.map(|msg| (Instant::now(), get_resident_set_size(), msg)), + start_and_message: message_and_format.map(|(msg, format)| { + (Instant::now(), get_resident_set_size(), msg, format, unique) + }), } } @@ -726,10 +758,10 @@ impl<'a> VerboseTimingGuard<'a> { impl Drop for VerboseTimingGuard<'_> { fn drop(&mut self) { - if let Some((start_time, start_rss, ref message)) = self.start_and_message { + if let Some((start_time, start_rss, ref message, format, unique)) = self.start_and_message { let end_rss = get_resident_set_size(); let dur = start_time.elapsed(); - print_time_passes_entry(message, dur, start_rss, end_rss); + print_time_passes_entry(message, dur, start_rss, end_rss, format, unique); } } } @@ -739,7 +771,21 @@ pub fn print_time_passes_entry( dur: Duration, start_rss: Option, end_rss: Option, + format: TimePassesFormat, + unique: bool, ) { + if format == TimePassesFormat::Json { + let json = json!({ + "pass": what, + "time": dur.as_secs_f64(), + "rss_start": start_rss, + "rss_end": end_rss, + "unique": unique, + }); + eprintln!("time: {}", json.to_string()); + return; + } + // Print the pass if its duration is greater than 5 ms, or it changed the // measured RSS. let is_notable = || { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 8634c64417654..d44bd7fdb1851 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -20,7 +20,9 @@ pub extern crate rustc_plugin_impl as plugin; use rustc_ast as ast; use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults}; -use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; +use rustc_data_structures::profiling::{ + get_resident_set_size, print_time_passes_entry, TimePassesFormat, +}; use rustc_data_structures::sync::SeqCst; use rustc_errors::registry::{InvalidErrorCode, Registry}; use rustc_errors::{ @@ -161,7 +163,7 @@ pub trait Callbacks { #[derive(Default)] pub struct TimePassesCallbacks { - time_passes: bool, + time_passes: Option, } impl Callbacks for TimePassesCallbacks { @@ -171,7 +173,8 @@ impl Callbacks for TimePassesCallbacks { // If a --print=... option has been given, we don't print the "total" // time because it will mess up the --print output. See #64339. // - self.time_passes = config.opts.prints.is_empty() && config.opts.unstable_opts.time_passes; + self.time_passes = (config.opts.prints.is_empty() && config.opts.unstable_opts.time_passes) + .then(|| config.opts.unstable_opts.time_passes_format); config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath; } } @@ -1354,9 +1357,9 @@ pub fn main() -> ! { RunCompiler::new(&args, &mut callbacks).run() }); - if callbacks.time_passes { + if let Some(format) = callbacks.time_passes { let end_rss = get_resident_set_size(); - print_time_passes_entry("total", start_time.elapsed(), start_rss, end_rss); + print_time_passes_entry("total", start_time.elapsed(), start_rss, end_rss, format, true); } process::exit(exit_code) diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 014810dba9cce..eb5990507fb6d 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -2,6 +2,7 @@ use crate::interface::parse_cfgspecs; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::profiling::TimePassesFormat; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; use rustc_session::config::rustc_optgroups; use rustc_session::config::Input; @@ -699,6 +700,7 @@ fn test_unstable_options_tracking_hash() { untracked!(threads, 99); untracked!(time_llvm_passes, true); untracked!(time_passes, true); + untracked!(time_passes_format, TimePassesFormat::Json); untracked!(trace_macros, true); untracked!(track_diagnostics, true); untracked!(trim_diagnostic_paths, false); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 0548379dc2fc1..c75af48e80af4 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -4,6 +4,7 @@ use crate::early_error; use crate::lint; use crate::search_paths::SearchPath; use crate::utils::NativeLib; +use rustc_data_structures::profiling::TimePassesFormat; use rustc_errors::{LanguageIdentifier, TerminalUrl}; use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet}; use rustc_target::spec::{ @@ -365,6 +366,7 @@ mod desc { pub const parse_number: &str = "a number"; pub const parse_opt_number: &str = parse_number; pub const parse_threads: &str = parse_number; + pub const parse_time_passes_format: &str = "`text` (default) or `json`"; pub const parse_passes: &str = "a space-separated list of passes, or `all`"; pub const parse_panic_strategy: &str = "either `unwind` or `abort`"; pub const parse_opt_panic_strategy: &str = parse_panic_strategy; @@ -829,6 +831,21 @@ mod parse { true } + pub(crate) fn parse_time_passes_format(slot: &mut TimePassesFormat, v: Option<&str>) -> bool { + match v { + None => true, + Some("json") => { + *slot = TimePassesFormat::Json; + true + } + Some("text") => { + *slot = TimePassesFormat::Text; + true + } + Some(_) => false, + } + } + pub(crate) fn parse_dump_mono_stats(slot: &mut DumpMonoStatsFormat, v: Option<&str>) -> bool { match v { None => true, @@ -1709,6 +1726,8 @@ options! { "measure time of each LLVM pass (default: no)"), time_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each rustc pass (default: no)"), + time_passes_format: TimePassesFormat = (TimePassesFormat::Text, parse_time_passes_format, [UNTRACKED], + "the format to use for -Z time-passes (`text` (default) or `json`)"), tiny_const_eval_limit: bool = (false, parse_bool, [TRACKED], "sets a tiny, non-configurable limit for const eval; useful for compiler tests"), #[rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field")] diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index fdacf814dd672..1a3fdc7f437c8 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1487,7 +1487,10 @@ pub fn build_session( CguReuseTracker::new_disabled() }; - let prof = SelfProfilerRef::new(self_profiler, sopts.unstable_opts.time_passes); + let prof = SelfProfilerRef::new( + self_profiler, + sopts.unstable_opts.time_passes.then(|| sopts.unstable_opts.time_passes_format), + ); let ctfe_backtrace = Lock::new(match env::var("RUSTC_CTFE_BACKTRACE") { Ok(ref val) if val == "immediate" => CtfeBacktrace::Immediate, diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index 3b3d4ca5d6b21..a7feb2794c4f6 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -4,10 +4,10 @@ use std::path::{Path, PathBuf}; impl Session { pub fn timer(&self, what: &'static str) -> VerboseTimingGuard<'_> { - self.prof.verbose_generic_activity(what) + self.prof.unique_verbose_generic_activity(what) } pub fn time(&self, what: &'static str, f: impl FnOnce() -> R) -> R { - self.prof.verbose_generic_activity(what).run(f) + self.prof.unique_verbose_generic_activity(what).run(f) } } diff --git a/tests/rustdoc-ui/z-help.stdout b/tests/rustdoc-ui/z-help.stdout index 5ad38e4fd9821..72f5f933d8db4 100644 --- a/tests/rustdoc-ui/z-help.stdout +++ b/tests/rustdoc-ui/z-help.stdout @@ -183,6 +183,7 @@ -Z threads=val -- use a thread pool with N threads -Z time-llvm-passes=val -- measure time of each LLVM pass (default: no) -Z time-passes=val -- measure time of each rustc pass (default: no) + -Z time-passes-format=val -- the format to use for -Z time-passes (`text` (default) or `json`) -Z tiny-const-eval-limit=val -- sets a tiny, non-configurable limit for const eval; useful for compiler tests -Z tls-model=val -- choose the TLS model to use (`rustc --print tls-models` for details) -Z trace-macros=val -- for every macro invocation, print its name and arguments (default: no) From 6c57dda44d2c9c12fdaab359c28767e4ccdb671b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 21 Mar 2023 18:41:45 +0100 Subject: [PATCH 11/15] Remove `unique` and move `VerboseTimingGuard` fields into a new struct --- compiler/rustc_codegen_ssa/src/base.rs | 1 - .../rustc_data_structures/src/profiling.rs | 60 +++++++++---------- compiler/rustc_driver_impl/src/lib.rs | 2 +- compiler/rustc_session/src/utils.rs | 4 +- 4 files changed, 32 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index a109633520ab9..24a83ac546f77 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -787,7 +787,6 @@ pub fn codegen_crate( start_rss.unwrap(), end_rss, tcx.sess.opts.unstable_opts.time_passes_format, - true, ); } diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 87b74903d5125..58a0609e29655 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -220,7 +220,7 @@ impl SelfProfilerRef { let message_and_format = self.print_verbose_generic_activities.map(|format| (event_label.to_owned(), format)); - VerboseTimingGuard::start(message_and_format, self.generic_activity(event_label), false) + VerboseTimingGuard::start(message_and_format, self.generic_activity(event_label)) } /// Like `verbose_generic_activity`, but with an extra arg. @@ -239,21 +239,9 @@ impl SelfProfilerRef { VerboseTimingGuard::start( message_and_format, self.generic_activity_with_arg(event_label, event_arg), - false, ) } - /// Like `verbose_generic_activity`, but `event_label` must be unique for a rustc session. - pub fn unique_verbose_generic_activity( - &self, - event_label: &'static str, - ) -> VerboseTimingGuard<'_> { - let message_and_format = - self.print_verbose_generic_activities.map(|format| (event_label.to_owned(), format)); - - VerboseTimingGuard::start(message_and_format, self.generic_activity(event_label), true) - } - /// Start profiling a generic activity. Profiling continues until the /// TimingGuard returned from this call is dropped. #[inline(always)] @@ -729,9 +717,16 @@ impl<'a> TimingGuard<'a> { } } +struct VerboseInfo { + start_time: Instant, + start_rss: Option, + message: String, + format: TimePassesFormat, +} + #[must_use] pub struct VerboseTimingGuard<'a> { - start_and_message: Option<(Instant, Option, String, TimePassesFormat, bool)>, + info: Option, _guard: TimingGuard<'a>, } @@ -739,12 +734,14 @@ impl<'a> VerboseTimingGuard<'a> { pub fn start( message_and_format: Option<(String, TimePassesFormat)>, _guard: TimingGuard<'a>, - unique: bool, ) -> Self { VerboseTimingGuard { _guard, - start_and_message: message_and_format.map(|(msg, format)| { - (Instant::now(), get_resident_set_size(), msg, format, unique) + info: message_and_format.map(|(message, format)| VerboseInfo { + start_time: Instant::now(), + start_rss: get_resident_set_size(), + message, + format, }), } } @@ -758,10 +755,10 @@ impl<'a> VerboseTimingGuard<'a> { impl Drop for VerboseTimingGuard<'_> { fn drop(&mut self) { - if let Some((start_time, start_rss, ref message, format, unique)) = self.start_and_message { + if let Some(info) = &self.info { let end_rss = get_resident_set_size(); - let dur = start_time.elapsed(); - print_time_passes_entry(message, dur, start_rss, end_rss, format, unique); + let dur = info.start_time.elapsed(); + print_time_passes_entry(&info.message, dur, info.start_rss, end_rss, info.format); } } } @@ -772,18 +769,19 @@ pub fn print_time_passes_entry( start_rss: Option, end_rss: Option, format: TimePassesFormat, - unique: bool, ) { - if format == TimePassesFormat::Json { - let json = json!({ - "pass": what, - "time": dur.as_secs_f64(), - "rss_start": start_rss, - "rss_end": end_rss, - "unique": unique, - }); - eprintln!("time: {}", json.to_string()); - return; + match format { + TimePassesFormat::Json => { + let json = json!({ + "pass": what, + "time": dur.as_secs_f64(), + "rss_start": start_rss, + "rss_end": end_rss, + }); + eprintln!("time: {}", json.to_string()); + return; + } + TimePassesFormat::Text => (), } // Print the pass if its duration is greater than 5 ms, or it changed the diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index d44bd7fdb1851..ad2c2b13fed57 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -1359,7 +1359,7 @@ pub fn main() -> ! { if let Some(format) = callbacks.time_passes { let end_rss = get_resident_set_size(); - print_time_passes_entry("total", start_time.elapsed(), start_rss, end_rss, format, true); + print_time_passes_entry("total", start_time.elapsed(), start_rss, end_rss, format); } process::exit(exit_code) diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index a7feb2794c4f6..3b3d4ca5d6b21 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -4,10 +4,10 @@ use std::path::{Path, PathBuf}; impl Session { pub fn timer(&self, what: &'static str) -> VerboseTimingGuard<'_> { - self.prof.unique_verbose_generic_activity(what) + self.prof.verbose_generic_activity(what) } pub fn time(&self, what: &'static str, f: impl FnOnce() -> R) -> R { - self.prof.unique_verbose_generic_activity(what).run(f) + self.prof.verbose_generic_activity(what).run(f) } } From 20f3f437d1f48706930cdb6e9e5089131a34d006 Mon Sep 17 00:00:00 2001 From: Mu42 Date: Wed, 22 Mar 2023 13:52:24 +0800 Subject: [PATCH 12/15] Fixes #109436: add parentheses properly --- .../src/traits/error_reporting/suggestions.rs | 35 +++++++++++++++---- tests/ui/suggestions/issue-109436.rs | 13 +++++++ tests/ui/suggestions/issue-109436.stderr | 15 ++++++++ 3 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 tests/ui/suggestions/issue-109436.rs create mode 100644 tests/ui/suggestions/issue-109436.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index b501840b9260b..eb2a87100d312 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1358,6 +1358,34 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { Applicability::MaybeIncorrect, ); } else { + let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut; + let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" }); + let sugg_msg = &format!( + "consider{} borrowing here", + if is_mut { " mutably" } else { "" } + ); + + // Issue #109436, we need to add parentheses properly for method calls + // for example, `foo.into()` should be `(&foo).into()` + if let Ok(snippet) = self + .tcx + .sess + .source_map() + .span_to_snippet(self.tcx.sess.source_map().next_point(span)) + { + if snippet == "." { + err.multipart_suggestion_verbose( + sugg_msg, + vec![ + (span.shrink_to_lo(), format!("({}", sugg_prefix)), + (span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); + return true; + } + } + // Issue #104961, we need to add parentheses properly for compond expressions // for example, `x.starts_with("hi".to_string() + "you")` // should be `x.starts_with(&("hi".to_string() + "you"))` @@ -1374,14 +1402,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { _ => false, }; - let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut; let span = if needs_parens { span } else { span.shrink_to_lo() }; - let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" }); - let sugg_msg = &format!( - "consider{} borrowing here", - if is_mut { " mutably" } else { "" } - ); - let suggestions = if !needs_parens { vec![(span.shrink_to_lo(), format!("{}", sugg_prefix))] } else { diff --git a/tests/ui/suggestions/issue-109436.rs b/tests/ui/suggestions/issue-109436.rs new file mode 100644 index 0000000000000..e45ee5991db28 --- /dev/null +++ b/tests/ui/suggestions/issue-109436.rs @@ -0,0 +1,13 @@ +struct Foo; +struct Bar; + +impl From<&Foo> for Bar { + fn from(foo: &Foo) -> Bar { + Bar + } +} + +fn main() { + let foo = Foo; + let b: Bar = foo.into(); //~ ERROR E0277 +} diff --git a/tests/ui/suggestions/issue-109436.stderr b/tests/ui/suggestions/issue-109436.stderr new file mode 100644 index 0000000000000..48518b33d12ac --- /dev/null +++ b/tests/ui/suggestions/issue-109436.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `Foo: Into<_>` is not satisfied + --> $DIR/issue-109436.rs:12:22 + | +LL | let b: Bar = foo.into(); + | ^^^^ the trait `~const Into<_>` is not implemented for `Foo` + | + = note: required for `Foo` to implement `Into` +help: consider borrowing here + | +LL | let b: Bar = (&foo).into(); + | ++ + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 825f0888cc168c767e3f6c071831963ddebbb78a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 22 Mar 2023 15:38:58 +0100 Subject: [PATCH 13/15] Move useless_anynous_reexport lint into unused_imports --- compiler/rustc_lint/messages.ftl | 3 - compiler/rustc_lint/src/lib.rs | 3 - compiler/rustc_lint/src/lints.rs | 8 --- compiler/rustc_lint/src/reexports.rs | 82 ---------------------- compiler/rustc_resolve/src/check_unused.rs | 42 ++++++++++- tests/ui/imports/issue-99695-b.fixed | 2 +- tests/ui/imports/issue-99695-b.rs | 2 +- tests/ui/imports/issue-99695.fixed | 2 +- tests/ui/imports/issue-99695.rs | 2 +- 9 files changed, 44 insertions(+), 102 deletions(-) delete mode 100644 compiler/rustc_lint/src/reexports.rs diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index c822237413c70..68e62c9789aed 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -508,6 +508,3 @@ lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its ass .specifically = this associated type bound is unsatisfied for `{$proj_ty}` lint_opaque_hidden_inferred_bound_sugg = add this bound - -lint_useless_anonymous_reexport = useless anonymous re-export - .note = only anonymous re-exports of traits are useful, this is {$article} `{$desc}` diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index c2cc2fcdf551a..b3578540516d0 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -74,7 +74,6 @@ mod opaque_hidden_inferred_bound; mod pass_by_value; mod passes; mod redundant_semicolon; -mod reexports; mod traits; mod types; mod unused; @@ -112,7 +111,6 @@ use noop_method_call::*; use opaque_hidden_inferred_bound::*; use pass_by_value::*; use redundant_semicolon::*; -use reexports::*; use traits::*; use types::*; use unused::*; @@ -244,7 +242,6 @@ late_lint_methods!( OpaqueHiddenInferredBound: OpaqueHiddenInferredBound, MultipleSupertraitUpcastable: MultipleSupertraitUpcastable, MapUnitFn: MapUnitFn, - UselessAnonymousReexport: UselessAnonymousReexport, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 46a025f41e04a..308c02929ca42 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1528,11 +1528,3 @@ pub struct UnusedAllocationDiag; #[derive(LintDiagnostic)] #[diag(lint_unused_allocation_mut)] pub struct UnusedAllocationMutDiag; - -#[derive(LintDiagnostic)] -#[diag(lint_useless_anonymous_reexport)] -#[note] -pub struct UselessAnonymousReexportDiag { - pub article: &'static str, - pub desc: &'static str, -} diff --git a/compiler/rustc_lint/src/reexports.rs b/compiler/rustc_lint/src/reexports.rs deleted file mode 100644 index 8737a57ea0264..0000000000000 --- a/compiler/rustc_lint/src/reexports.rs +++ /dev/null @@ -1,82 +0,0 @@ -use crate::lints::UselessAnonymousReexportDiag; -use crate::{LateContext, LateLintPass, LintContext}; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; -use rustc_hir::{Item, ItemKind, UseKind}; -use rustc_middle::ty::Visibility; -use rustc_span::symbol::kw; -use rustc_span::Span; - -declare_lint! { - /// The `useless_anonymous_reexport` lint checks if anonymous re-exports - /// are re-exports of traits. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #![deny(useless_anonymous_reexport)] - /// - /// mod sub { - /// pub struct Bar; - /// } - /// - /// pub use self::sub::Bar as _; - /// # fn main() {} - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Anonymous re-exports are only useful if it's a re-export of a trait - /// in case you want to give access to it. If you re-export any other kind, - /// you won't be able to use it since its name won't be accessible. - pub USELESS_ANONYMOUS_REEXPORT, - Warn, - "useless anonymous re-export" -} - -declare_lint_pass!(UselessAnonymousReexport => [USELESS_ANONYMOUS_REEXPORT]); - -fn emit_err(cx: &LateContext<'_>, span: Span, def_id: DefId) { - let article = cx.tcx.def_descr_article(def_id); - let desc = cx.tcx.def_descr(def_id); - cx.emit_spanned_lint( - USELESS_ANONYMOUS_REEXPORT, - span, - UselessAnonymousReexportDiag { article, desc }, - ); -} - -impl<'tcx> LateLintPass<'tcx> for UselessAnonymousReexport { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Use(path, kind) = item.kind && - !matches!(kind, UseKind::Glob) && - item.ident.name == kw::Underscore && - // We only want re-exports. If it's just a `use X;`, then we ignore it. - match cx.tcx.local_visibility(item.owner_id.def_id) { - Visibility::Public => true, - Visibility::Restricted(level) => { - level != cx.tcx.parent_module_from_def_id(item.owner_id.def_id) - } - } - { - for def_id in path.res.iter().filter_map(|r| r.opt_def_id()) { - match cx.tcx.def_kind(def_id) { - DefKind::Trait | DefKind::TraitAlias => {} - DefKind::TyAlias => { - let ty = cx.tcx.type_of(def_id); - if !ty.0.is_trait() { - emit_err(cx, item.span, def_id); - break; - } - } - _ => { - emit_err(cx, item.span, def_id); - break; - } - } - } - } - } -} diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index b2578e4c4b446..dbf6cec788b5a 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -32,9 +32,10 @@ use rustc_ast::visit::{self, Visitor}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::unord::UnordSet; use rustc_errors::{pluralize, MultiSpan}; +use rustc_hir::def::{DefKind, Res}; use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES, UNUSED_IMPORTS}; use rustc_session::lint::BuiltinLintDiagnostics; -use rustc_span::symbol::Ident; +use rustc_span::symbol::{kw, Ident}; use rustc_span::{Span, DUMMY_SP}; struct UnusedImport<'a> { @@ -58,6 +59,7 @@ struct UnusedImportCheckVisitor<'a, 'b, 'tcx> { base_use_tree: Option<&'a ast::UseTree>, base_id: ast::NodeId, item_span: Span, + base_use_is_pub: bool, } struct ExternCrateToLint { @@ -110,6 +112,35 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> { unused: Default::default(), }) } + + fn check_import_as_underscore(&mut self, item: &ast::UseTree, id: ast::NodeId) { + match item.kind { + ast::UseTreeKind::Simple(Some(ident)) => { + if ident.name == kw::Underscore + && !self + .r + .import_res_map + .get(&id) + .map(|per_ns| { + per_ns.iter().filter_map(|res| res.as_ref()).any(|res| { + matches!(res, Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) + }) + }) + .unwrap_or(false) + { + self.unused_import(self.base_id).add(id); + } + } + ast::UseTreeKind::Nested(ref items) => self.check_imports_as_underscore(items), + _ => {} + } + } + + fn check_imports_as_underscore(&mut self, items: &[(ast::UseTree, ast::NodeId)]) { + for (item, id) in items { + self.check_import_as_underscore(item, *id); + } + } } impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> { @@ -119,7 +150,8 @@ impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> { // whether they're used or not. Also ignore imports with a dummy span // because this means that they were generated in some fashion by the // compiler and we don't need to consider them. - ast::ItemKind::Use(..) if item.vis.kind.is_pub() || item.span.is_dummy() => return, + ast::ItemKind::Use(..) if item.span.is_dummy() => return, + ast::ItemKind::Use(..) => self.base_use_is_pub = item.vis.kind.is_pub(), ast::ItemKind::ExternCrate(orig_name) => { self.extern_crate_items.push(ExternCrateToLint { id: item.id, @@ -146,6 +178,11 @@ impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> { self.base_use_tree = Some(use_tree); } + if self.base_use_is_pub { + self.check_import_as_underscore(use_tree, id); + return; + } + if let ast::UseTreeKind::Nested(ref items) = use_tree.kind { if items.is_empty() { self.unused_import(self.base_id).add(id); @@ -300,6 +337,7 @@ impl Resolver<'_, '_> { base_use_tree: None, base_id: ast::DUMMY_NODE_ID, item_span: DUMMY_SP, + base_use_is_pub: false, }; visit::walk_crate(&mut visitor, krate); diff --git a/tests/ui/imports/issue-99695-b.fixed b/tests/ui/imports/issue-99695-b.fixed index 0f688fa282356..0e60c73b67a44 100644 --- a/tests/ui/imports/issue-99695-b.fixed +++ b/tests/ui/imports/issue-99695-b.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused, nonstandard_style, useless_anonymous_reexport)] +#![allow(unused, nonstandard_style)] mod m { mod p { diff --git a/tests/ui/imports/issue-99695-b.rs b/tests/ui/imports/issue-99695-b.rs index b433997e53f6a..031443a1f5df8 100644 --- a/tests/ui/imports/issue-99695-b.rs +++ b/tests/ui/imports/issue-99695-b.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused, nonstandard_style, useless_anonymous_reexport)] +#![allow(unused, nonstandard_style)] mod m { mod p { diff --git a/tests/ui/imports/issue-99695.fixed b/tests/ui/imports/issue-99695.fixed index 17ff409324e34..6bf228b23aad2 100644 --- a/tests/ui/imports/issue-99695.fixed +++ b/tests/ui/imports/issue-99695.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused, nonstandard_style, useless_anonymous_reexport)] +#![allow(unused, nonstandard_style)] mod m { #[macro_export] macro_rules! nu { diff --git a/tests/ui/imports/issue-99695.rs b/tests/ui/imports/issue-99695.rs index b8979bcb7345c..f7199f1497ab0 100644 --- a/tests/ui/imports/issue-99695.rs +++ b/tests/ui/imports/issue-99695.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused, nonstandard_style, useless_anonymous_reexport)] +#![allow(unused, nonstandard_style)] mod m { #[macro_export] macro_rules! nu { From e03b13ccb7726005a49c5a9c6a3b47c8ba6804e6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 22 Mar 2023 15:39:16 +0100 Subject: [PATCH 14/15] Update anonymous-reexport UI test --- tests/ui/lint/anonymous-reexport.rs | 16 ++++---- tests/ui/lint/anonymous-reexport.stderr | 49 ++++++++++--------------- 2 files changed, 26 insertions(+), 39 deletions(-) diff --git a/tests/ui/lint/anonymous-reexport.rs b/tests/ui/lint/anonymous-reexport.rs index 5d56ae6f969b5..11ac5d07140ba 100644 --- a/tests/ui/lint/anonymous-reexport.rs +++ b/tests/ui/lint/anonymous-reexport.rs @@ -1,4 +1,4 @@ -#![deny(useless_anonymous_reexport)] +#![deny(unused_imports)] #![crate_type = "rlib"] mod my_mod { @@ -9,13 +9,11 @@ mod my_mod { } pub use self::my_mod::Foo as _; -pub use self::my_mod::TyFoo as _; -pub use self::my_mod::Bar as _; //~ ERROR -pub use self::my_mod::TyBar as _; //~ ERROR -pub use self::my_mod::{Bar as _}; //~ ERROR -pub use self::my_mod::{Bar as _, Foo as _}; //~ ERROR -pub use self::my_mod::{Bar as _, TyBar as _}; -//~^ ERROR -//~| ERROR +pub use self::my_mod::TyFoo as _; //~ ERROR unused import +pub use self::my_mod::Bar as _; //~ ERROR unused import +pub use self::my_mod::TyBar as _; //~ ERROR unused import +pub use self::my_mod::{Bar as _}; //~ ERROR unused import +pub use self::my_mod::{Bar as _, Foo as _}; //~ ERROR unused import +pub use self::my_mod::{Bar as _, TyBar as _}; //~ ERROR unused imports #[allow(unused_imports)] use self::my_mod::TyBar as _; diff --git a/tests/ui/lint/anonymous-reexport.stderr b/tests/ui/lint/anonymous-reexport.stderr index f4f8b41c417a4..e3854a5459ec0 100644 --- a/tests/ui/lint/anonymous-reexport.stderr +++ b/tests/ui/lint/anonymous-reexport.stderr @@ -1,55 +1,44 @@ -error: useless anonymous re-export - --> $DIR/anonymous-reexport.rs:13:1 +error: unused import: `self::my_mod::TyFoo as _` + --> $DIR/anonymous-reexport.rs:12:9 | -LL | pub use self::my_mod::Bar as _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub use self::my_mod::TyFoo as _; + | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only anonymous re-exports of traits are useful, this is a `struct` note: the lint level is defined here --> $DIR/anonymous-reexport.rs:1:9 | -LL | #![deny(useless_anonymous_reexport)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(unused_imports)] + | ^^^^^^^^^^^^^^ -error: useless anonymous re-export - --> $DIR/anonymous-reexport.rs:14:1 +error: unused import: `self::my_mod::Bar as _` + --> $DIR/anonymous-reexport.rs:13:9 | -LL | pub use self::my_mod::TyBar as _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub use self::my_mod::Bar as _; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: unused import: `self::my_mod::TyBar as _` + --> $DIR/anonymous-reexport.rs:14:9 | - = note: only anonymous re-exports of traits are useful, this is a `type alias` +LL | pub use self::my_mod::TyBar as _; + | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: useless anonymous re-export +error: unused import: `Bar as _` --> $DIR/anonymous-reexport.rs:15:24 | LL | pub use self::my_mod::{Bar as _}; | ^^^^^^^^ - | - = note: only anonymous re-exports of traits are useful, this is a `struct` -error: useless anonymous re-export +error: unused import: `Bar as _` --> $DIR/anonymous-reexport.rs:16:24 | LL | pub use self::my_mod::{Bar as _, Foo as _}; | ^^^^^^^^ - | - = note: only anonymous re-exports of traits are useful, this is a `struct` -error: useless anonymous re-export +error: unused imports: `Bar as _`, `TyBar as _` --> $DIR/anonymous-reexport.rs:17:24 | LL | pub use self::my_mod::{Bar as _, TyBar as _}; - | ^^^^^^^^ - | - = note: only anonymous re-exports of traits are useful, this is a `struct` - -error: useless anonymous re-export - --> $DIR/anonymous-reexport.rs:17:34 - | -LL | pub use self::my_mod::{Bar as _, TyBar as _}; - | ^^^^^^^^^^ - | - = note: only anonymous re-exports of traits are useful, this is a `type alias` + | ^^^^^^^^ ^^^^^^^^^^ error: aborting due to 6 previous errors From 2580348ca35270b5cefbd0f62ff4e6ccd44ef3be Mon Sep 17 00:00:00 2001 From: Mu42 Date: Thu, 23 Mar 2023 09:41:49 +0800 Subject: [PATCH 15/15] Use span_look_ahead instead of next_point --- .../src/traits/error_reporting/suggestions.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index eb2a87100d312..4a45d9b9befb4 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1367,12 +1367,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Issue #109436, we need to add parentheses properly for method calls // for example, `foo.into()` should be `(&foo).into()` - if let Ok(snippet) = self - .tcx - .sess - .source_map() - .span_to_snippet(self.tcx.sess.source_map().next_point(span)) - { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet( + self.tcx.sess.source_map().span_look_ahead(span, Some("."), Some(50)), + ) { if snippet == "." { err.multipart_suggestion_verbose( sugg_msg,