From 4854fc96463b69174909e56c10805f36bd6d66a9 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Sat, 15 Jul 2023 20:41:27 +0200 Subject: [PATCH 001/250] Adds thread::sleep_until, tracking issue TODO APC (API change proposal): https://github.com/rust-lang/libs-team/issues/237 --- library/std/src/thread/mod.rs | 66 +++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index e4581c2de78..7cbd7a8a2f6 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -872,6 +872,72 @@ pub fn sleep(dur: Duration) { imp::Thread::sleep(dur) } +/// Puts the current thread to sleep until the specified deadline has passed. +/// +/// The thread may still be asleep after the deadline specified due to +/// scheduling specifics or platform-dependent functionality. It will never +/// wake before. +/// +/// This function is blocking, and should not be used in `async` functions. +/// +/// # Platform-specific behavior +/// +/// This function uses ['sleep'] internally, see its platform-specific behaviour. +/// +/// +/// # Examples +/// +/// A simple game loop that limits the game to 60 frames per second. +/// +/// '''no_run +/// # use std::time::{Duration, Instant}; +/// # use std::thread; +/// # +/// let max_fps = 60.0; +/// let frame_time = Duration::from_secs_f32(1.0/max_fps); +/// let mut next_frame = Instant::now(); +/// loop { +/// thread::sleep_until(next_frame); +/// next_frame += frame_time; +/// update(); +/// render(); +/// } +/// ''' +/// +/// A slow api we must not call too fast and which takes a few +/// tries before succeeding. By using `sleep_until` the time the +/// api call takes does not influence when we retry or when we give up +/// +/// ```no_run +/// # use std::time::{Duration, Instant}; +/// # use std::thread; +/// # +/// # const MAX_DURATION: Duration = Duration::from_secs(10); +/// # +/// let deadline = Instant::now() + MAX_DURATION; +/// let delay = Duration::from_millis(250); +/// let mut next_attempt = Instant::now(); +/// loop { +/// if Instant::now() > deadline { +/// break Err(()), +/// } +/// if let Ready(data) = slow_web_api_call() { +/// break Ok(data), +/// } +/// +/// next_attempt = deadline.min(next_attempt + delay); +/// thread::sleep_until(next_attempt); +/// } +/// ``` +#[unstable(feature = "thread_sleep_until", issue = "todo")] +pub fn sleep_untill(deadline: Instant) { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + thread::sleep(delay); + } +} + /// Used to ensure that `park` and `park_timeout` do not unwind, as that can /// cause undefined behaviour if not handled correctly (see #102398 for context). struct PanicGuard; From 77844f015f0f3277517f1a5f18404d0777a60274 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Sun, 16 Jul 2023 11:32:22 +0200 Subject: [PATCH 002/250] fixes sleep_until examples --- library/std/src/thread/mod.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 7cbd7a8a2f6..35f4cb64178 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -893,6 +893,9 @@ pub fn sleep(dur: Duration) { /// # use std::time::{Duration, Instant}; /// # use std::thread; /// # +/// # fn update() {} +/// # fn render() {} +/// # /// let max_fps = 60.0; /// let frame_time = Duration::from_secs_f32(1.0/max_fps); /// let mut next_frame = Instant::now(); @@ -912,6 +915,8 @@ pub fn sleep(dur: Duration) { /// # use std::time::{Duration, Instant}; /// # use std::thread; /// # +/// # fn slow_web_api_call() {} +/// # /// # const MAX_DURATION: Duration = Duration::from_secs(10); /// # /// let deadline = Instant::now() + MAX_DURATION; @@ -919,10 +924,10 @@ pub fn sleep(dur: Duration) { /// let mut next_attempt = Instant::now(); /// loop { /// if Instant::now() > deadline { -/// break Err(()), +/// break Err(()); /// } /// if let Ready(data) = slow_web_api_call() { -/// break Ok(data), +/// break Ok(data); /// } /// /// next_attempt = deadline.min(next_attempt + delay); From bd70b639fde23f599858d81b0ffa8076d2c518bd Mon Sep 17 00:00:00 2001 From: dvdsk Date: Sun, 16 Jul 2023 12:16:04 +0200 Subject: [PATCH 003/250] fix examples add tracking issue --- library/std/src/thread/mod.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 35f4cb64178..9eb3193fd9f 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -178,7 +178,7 @@ use crate::sys_common::thread; use crate::sys_common::thread_info; use crate::sys_common::thread_parking::Parker; use crate::sys_common::{AsInner, IntoInner}; -use crate::time::Duration; +use crate::time::{Duration, Instant}; #[stable(feature = "scoped_threads", since = "1.63.0")] mod scoped; @@ -882,14 +882,14 @@ pub fn sleep(dur: Duration) { /// /// # Platform-specific behavior /// -/// This function uses ['sleep'] internally, see its platform-specific behaviour. +/// This function uses [`sleep`] internally, see its platform-specific behaviour. /// /// /// # Examples /// /// A simple game loop that limits the game to 60 frames per second. /// -/// '''no_run +/// ```no_run /// # use std::time::{Duration, Instant}; /// # use std::thread; /// # @@ -905,7 +905,7 @@ pub fn sleep(dur: Duration) { /// update(); /// render(); /// } -/// ''' +/// ``` /// /// A slow api we must not call too fast and which takes a few /// tries before succeeding. By using `sleep_until` the time the @@ -915,10 +915,15 @@ pub fn sleep(dur: Duration) { /// # use std::time::{Duration, Instant}; /// # use std::thread; /// # -/// # fn slow_web_api_call() {} +/// # enum Status { +/// # Ready(usize), +/// # Waiting, +/// # } +/// # fn slow_web_api_call() -> Status { Status::Ready(42) } /// # /// # const MAX_DURATION: Duration = Duration::from_secs(10); /// # +/// # fn try_api_call() -> Result { /// let deadline = Instant::now() + MAX_DURATION; /// let delay = Duration::from_millis(250); /// let mut next_attempt = Instant::now(); @@ -926,20 +931,22 @@ pub fn sleep(dur: Duration) { /// if Instant::now() > deadline { /// break Err(()); /// } -/// if let Ready(data) = slow_web_api_call() { +/// if let Status::Ready(data) = slow_web_api_call() { /// break Ok(data); /// } /// /// next_attempt = deadline.min(next_attempt + delay); /// thread::sleep_until(next_attempt); /// } +/// # } +/// # let _data = try_api_call(); /// ``` -#[unstable(feature = "thread_sleep_until", issue = "todo")] -pub fn sleep_untill(deadline: Instant) { +#[unstable(feature = "thread_sleep_until", issue = "113752")] +pub fn sleep_until(deadline: Instant) { let now = Instant::now(); if let Some(delay) = deadline.checked_duration_since(now) { - thread::sleep(delay); + sleep(delay); } } From 4cf66f0b09c6bd917401ef69aea2b287dca49a53 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Sun, 16 Jul 2023 23:35:38 +0200 Subject: [PATCH 004/250] adds crate attribute to examples so they compile --- library/std/src/thread/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 9eb3193fd9f..7b26068c294 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -890,6 +890,7 @@ pub fn sleep(dur: Duration) { /// A simple game loop that limits the game to 60 frames per second. /// /// ```no_run +/// #![feature(thread_sleep_until)] /// # use std::time::{Duration, Instant}; /// # use std::thread; /// # @@ -912,6 +913,7 @@ pub fn sleep(dur: Duration) { /// api call takes does not influence when we retry or when we give up /// /// ```no_run +/// #![feature(thread_sleep_until)] /// # use std::time::{Duration, Instant}; /// # use std::thread; /// # From c5d4f7339ad576502f0a27a4bb8e917509000099 Mon Sep 17 00:00:00 2001 From: Alex Zepeda Date: Wed, 2 Aug 2023 17:57:13 -0700 Subject: [PATCH 005/250] proc-macro-test: Pass target to cargo invocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When cross compiling macos → dragonfly the dist build fails in the proc-maro-test-impl crate with the following error: ld: unknown option: -z\nclang: error: linker command failed with exit code 1 (use -v to see invocation) This appears to be a wart stemming from using an Apple host for cross compiling. Passing the target along to cargo allows it to pick up a linker that it understands and DTRT. --- crates/proc-macro-test/build.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/proc-macro-test/build.rs b/crates/proc-macro-test/build.rs index 19a5caa4ccd..7827157865a 100644 --- a/crates/proc-macro-test/build.rs +++ b/crates/proc-macro-test/build.rs @@ -71,6 +71,10 @@ fn main() { .arg("--target-dir") .arg(&target_dir); + if let Ok(target) = std::env::var("TARGET") { + cmd.args(["--target", &target]); + } + println!("Running {cmd:?}"); let output = cmd.output().unwrap(); From 8cfa4fe6b2268385480dc4a88ce3e6fdb06a3596 Mon Sep 17 00:00:00 2001 From: est31 Date: Sat, 5 Aug 2023 04:54:23 +0200 Subject: [PATCH 006/250] Add #[rustc_never_returns_null_ptr] And look for it in the useless_ptr_null_checks lint --- compiler/rustc_feature/src/builtin_attrs.rs | 4 ++++ compiler/rustc_lint/src/ptr_nulls.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 3 +++ compiler/rustc_span/src/symbol.rs | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index a183cfd8776..6596e47f615 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -698,6 +698,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_pass_by_value, Normal, template!(Word), ErrorFollowing, "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference." ), + rustc_attr!( + rustc_never_returns_null_ptr, Normal, template!(Word), ErrorFollowing, + "#[rustc_never_returns_null_ptr] is used to mark functions returning non-null pointers." + ), rustc_attr!( rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, @only_local: true, "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`." diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs index 02aff91032f..52d8099091a 100644 --- a/compiler/rustc_lint/src/ptr_nulls.rs +++ b/compiler/rustc_lint/src/ptr_nulls.rs @@ -49,7 +49,7 @@ fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&' } else if let ExprKind::Call(path, [arg]) = e.kind && let ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() - && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_from_ref | sym::ptr_from_mut)) { + && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr) { had_at_least_one_cast = true; arg } else if had_at_least_one_cast { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index cbb030958c6..bec12ca185e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -139,6 +139,9 @@ impl CheckAttrVisitor<'_> { self.check_rustc_std_internal_symbol(&attr, span, target) } sym::naked => self.check_naked(hir_id, attr, span, target), + sym::rustc_never_returns_null_ptr => { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } sym::rustc_legacy_const_generics => { self.check_rustc_legacy_const_generics(hir_id, &attr, span, target, item) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 44820ae6f72..07d48170add 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1311,6 +1311,7 @@ symbols! { rustc_main, rustc_mir, rustc_must_implement_one_of, + rustc_never_returns_null_ptr, rustc_nonnull_optimization_guaranteed, rustc_nounwind, rustc_object_lifetime_default, From 33970db8c6a50a4a8c0fbb3d7d94893de24b98b5 Mon Sep 17 00:00:00 2001 From: est31 Date: Sat, 5 Aug 2023 14:01:56 +0200 Subject: [PATCH 007/250] Add #[rustc_never_returns_null_ptr] to std functions Add the attribute to standard library functions that are guaranteed to never return null pointers, as their originating data wouldn't allow it. --- library/alloc/src/rc.rs | 2 ++ library/alloc/src/sync.rs | 2 ++ library/alloc/src/vec/mod.rs | 2 ++ library/core/src/cell.rs | 4 ++++ library/core/src/ffi/c_str.rs | 1 + library/core/src/ptr/mod.rs | 2 ++ library/core/src/ptr/non_null.rs | 2 ++ library/core/src/slice/mod.rs | 2 ++ library/core/src/str/mod.rs | 2 ++ library/core/src/sync/atomic.rs | 3 +++ 10 files changed, 22 insertions(+) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 60b07485c3a..c6693626e0a 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1304,6 +1304,7 @@ impl Rc { /// assert_eq!(unsafe { &*x_ptr }, "hello"); /// ``` #[stable(feature = "rc_raw", since = "1.17.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] pub fn into_raw(this: Self) -> *const T { let ptr = Self::as_ptr(&this); mem::forget(this); @@ -1327,6 +1328,7 @@ impl Rc { /// assert_eq!(unsafe { &*x_ptr }, "hello"); /// ``` #[stable(feature = "weak_into_raw", since = "1.45.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] pub fn as_ptr(this: &Self) -> *const T { let ptr: *mut RcBox = NonNull::as_ptr(this.ptr); diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 6c701225a84..d085466d847 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1455,6 +1455,7 @@ impl Arc { /// ``` #[must_use = "losing the pointer will leak memory"] #[stable(feature = "rc_raw", since = "1.17.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] pub fn into_raw(this: Self) -> *const T { let ptr = Self::as_ptr(&this); mem::forget(this); @@ -1479,6 +1480,7 @@ impl Arc { /// ``` #[must_use] #[stable(feature = "rc_as_ptr", since = "1.45.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] pub fn as_ptr(this: &Self) -> *const T { let ptr: *mut ArcInner = NonNull::as_ptr(this.ptr); diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index e45ddc7896b..a7bd8fdd424 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1233,6 +1233,7 @@ impl Vec { /// /// [`as_mut_ptr`]: Vec::as_mut_ptr #[stable(feature = "vec_as_ptr", since = "1.37.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] #[inline] pub fn as_ptr(&self) -> *const T { // We shadow the slice method of the same name to avoid going through @@ -1266,6 +1267,7 @@ impl Vec { /// assert_eq!(&*x, &[0, 1, 2, 3]); /// ``` #[stable(feature = "vec_as_ptr", since = "1.37.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] #[inline] pub fn as_mut_ptr(&mut self) -> *mut T { // We shadow the slice method of the same name to avoid going through diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index bf4c682d33e..82e24644414 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -543,6 +543,7 @@ impl Cell { #[inline] #[stable(feature = "cell_as_ptr", since = "1.12.0")] #[rustc_const_stable(feature = "const_cell_as_ptr", since = "1.32.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] pub const fn as_ptr(&self) -> *mut T { self.value.get() } @@ -1076,6 +1077,7 @@ impl RefCell { /// ``` #[inline] #[stable(feature = "cell_as_ptr", since = "1.12.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] pub fn as_ptr(&self) -> *mut T { self.value.get() } @@ -2071,6 +2073,7 @@ impl UnsafeCell { #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_unsafecell_get", since = "1.32.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] pub const fn get(&self) -> *mut T { // We can just cast the pointer from `UnsafeCell` to `T` because of // #[repr(transparent)]. This exploits std's special status, there is @@ -2213,6 +2216,7 @@ impl SyncUnsafeCell { /// when casting to `&mut T`, and ensure that there are no mutations /// or mutable aliases going on when casting to `&T` #[inline] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] pub const fn get(&self) -> *mut T { self.value.get() } diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index b59ec12790d..388a545d3d9 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -509,6 +509,7 @@ impl CStr { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_str_as_ptr", since = "1.32.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] pub const fn as_ptr(&self) -> *const c_char { self.inner.as_ptr() } diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 5f094ac4e7e..84cc2e474a1 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -698,6 +698,7 @@ where #[inline(always)] #[must_use] #[unstable(feature = "ptr_from_ref", issue = "106116")] +#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] #[rustc_diagnostic_item = "ptr_from_ref"] pub const fn from_ref(r: &T) -> *const T { r @@ -710,6 +711,7 @@ pub const fn from_ref(r: &T) -> *const T { #[inline(always)] #[must_use] #[unstable(feature = "ptr_from_ref", issue = "106116")] +#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] #[rustc_diagnostic_item = "ptr_from_mut"] pub const fn from_mut(r: &mut T) -> *mut T { r diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 9582ca9e0be..77a2e0775a3 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -320,6 +320,7 @@ impl NonNull { /// ``` #[stable(feature = "nonnull", since = "1.25.0")] #[rustc_const_stable(feature = "const_nonnull_as_ptr", since = "1.32.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] #[must_use] #[inline(always)] pub const fn as_ptr(self) -> *mut T { @@ -579,6 +580,7 @@ impl NonNull<[T]> { #[must_use] #[unstable(feature = "slice_ptr_get", issue = "74265")] #[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] pub const fn as_mut_ptr(self) -> *mut T { self.as_non_null_ptr().as_ptr() } diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index d95662afddd..0d635aced85 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -730,6 +730,7 @@ impl [T] { /// [`as_mut_ptr`]: slice::as_mut_ptr #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_slice_as_ptr", since = "1.32.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] #[inline(always)] #[must_use] pub const fn as_ptr(&self) -> *const T { @@ -760,6 +761,7 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] #[rustc_allow_const_fn_unstable(const_mut_refs)] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] #[inline(always)] #[must_use] pub const fn as_mut_ptr(&mut self) -> *mut T { diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 71c03f7bfc5..569707a40f3 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -387,6 +387,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "rustc_str_as_ptr", since = "1.32.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] #[must_use] #[inline(always)] pub const fn as_ptr(&self) -> *const u8 { @@ -402,6 +403,7 @@ impl str { /// It is your responsibility to make sure that the string slice only gets /// modified in a way that it remains valid UTF-8. #[stable(feature = "str_as_mut_ptr", since = "1.36.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] #[must_use] #[inline(always)] pub fn as_mut_ptr(&mut self) -> *mut u8 { diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 22a1c09782c..cf1fbe2d389 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1018,6 +1018,7 @@ impl AtomicBool { #[inline] #[stable(feature = "atomic_as_ptr", since = "1.70.0")] #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] pub const fn as_ptr(&self) -> *mut bool { self.v.get().cast() } @@ -1953,6 +1954,7 @@ impl AtomicPtr { #[inline] #[stable(feature = "atomic_as_ptr", since = "1.70.0")] #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] pub const fn as_ptr(&self) -> *mut *mut T { self.p.get() } @@ -2891,6 +2893,7 @@ macro_rules! atomic_int { #[inline] #[stable(feature = "atomic_as_ptr", since = "1.70.0")] #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")] + #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] pub const fn as_ptr(&self) -> *mut $int_type { self.v.get() } From 8faac74e54e8a76cd4ff860c1b6da3836e4a1b1e Mon Sep 17 00:00:00 2001 From: est31 Date: Sat, 5 Aug 2023 05:14:32 +0200 Subject: [PATCH 008/250] Remove ptr_from_mut diagnostic item It was added by #113657 for its purposes. Now it is not used any more, remove it, as we use the attr now. --- compiler/rustc_span/src/symbol.rs | 1 - library/core/src/ptr/mod.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 07d48170add..46f3487475b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1159,7 +1159,6 @@ symbols! { ptr_cast, ptr_cast_mut, ptr_const_is_null, - ptr_from_mut, ptr_from_ref, ptr_guaranteed_cmp, ptr_is_null, diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 84cc2e474a1..8d1a4de9de6 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -712,7 +712,6 @@ pub const fn from_ref(r: &T) -> *const T { #[must_use] #[unstable(feature = "ptr_from_ref", issue = "106116")] #[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)] -#[rustc_diagnostic_item = "ptr_from_mut"] pub const fn from_mut(r: &mut T) -> *mut T { r } From 4b1bc2701067d7b340d102d40227091c00447f48 Mon Sep 17 00:00:00 2001 From: est31 Date: Sun, 6 Aug 2023 01:25:24 +0200 Subject: [PATCH 009/250] Improve diagnostics and add tests for function calls --- compiler/rustc_lint/messages.ftl | 2 ++ compiler/rustc_lint/src/lints.rs | 2 ++ compiler/rustc_lint/src/ptr_nulls.rs | 52 ++++++++++++++++------------ tests/ui/lint/ptr_null_checks.rs | 14 +++++--- tests/ui/lint/ptr_null_checks.stderr | 38 +++++++++++--------- 5 files changed, 64 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index c4a7f717840..61f68db2cc0 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -457,6 +457,8 @@ lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking th .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value .label = expression has type `{$orig_ty}` +lint_ptr_null_checks_fn_ret = returned pointer of `{$fn_name}` call is never null, so checking it for null will always return false + lint_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false .label = expression has type `{$orig_ty}` diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 70311a5c576..049a7c3f7fa 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -634,6 +634,8 @@ pub enum PtrNullChecksDiag<'a> { #[label] label: Span, }, + #[diag(lint_ptr_null_checks_fn_ret)] + FnRet { fn_name: Ident }, } // for_loops_over_fallibles.rs diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs index 52d8099091a..0de72d8d3db 100644 --- a/compiler/rustc_lint/src/ptr_nulls.rs +++ b/compiler/rustc_lint/src/ptr_nulls.rs @@ -31,12 +31,30 @@ declare_lint! { declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]); -/// This function detects and returns the original expression from a series of consecutive casts, -/// ie. `(my_fn as *const _ as *mut _).cast_mut()` would return the expression for `my_fn`. -fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&'a Expr<'a>> { +/// This function checks if the expression is from a series of consecutive casts, +/// ie. `(my_fn as *const _ as *mut _).cast_mut()` and whether the original expression is either +/// a fn ptr, a reference, or a function call whose definition is +/// annotated with `#![rustc_never_returns_null_ptr]`. +/// If this situation is present, the function returns the appropriate diagnostic. +fn incorrect_check<'a, 'tcx: 'a>( + cx: &'a LateContext<'tcx>, + mut e: &'a Expr<'a>, +) -> Option> { let mut had_at_least_one_cast = false; loop { e = e.peel_blocks(); + if let ExprKind::MethodCall(_, _expr, [], _) = e.kind + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) + && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr) + && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) { + return Some(PtrNullChecksDiag::FnRet { fn_name }); + } else if let ExprKind::Call(path, _args) = e.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr) + && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) { + return Some(PtrNullChecksDiag::FnRet { fn_name }); + } e = if let ExprKind::Cast(expr, t) = e.kind && let TyKind::Ptr(_) = t.kind { had_at_least_one_cast = true; @@ -46,33 +64,21 @@ fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&' && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_cast | sym::ptr_cast_mut)) { had_at_least_one_cast = true; expr - } else if let ExprKind::Call(path, [arg]) = e.kind - && let ExprKind::Path(ref qpath) = path.kind - && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() - && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr) { - had_at_least_one_cast = true; - arg } else if had_at_least_one_cast { - return Some(e); + let orig_ty = cx.typeck_results().expr_ty(e); + return if orig_ty.is_fn() { + Some(PtrNullChecksDiag::FnPtr { orig_ty, label: e.span }) + } else if orig_ty.is_ref() { + Some(PtrNullChecksDiag::Ref { orig_ty, label: e.span }) + } else { + None + }; } else { return None; }; } } -fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option> { - let expr = ptr_cast_chain(cx, expr)?; - - let orig_ty = cx.typeck_results().expr_ty(expr); - if orig_ty.is_fn() { - Some(PtrNullChecksDiag::FnPtr { orig_ty, label: expr.span }) - } else if orig_ty.is_ref() { - Some(PtrNullChecksDiag::Ref { orig_ty, label: expr.span }) - } else { - None - } -} - impl<'tcx> LateLintPass<'tcx> for PtrNullChecks { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { match expr.kind { diff --git a/tests/ui/lint/ptr_null_checks.rs b/tests/ui/lint/ptr_null_checks.rs index e677ea3094d..3028084e962 100644 --- a/tests/ui/lint/ptr_null_checks.rs +++ b/tests/ui/lint/ptr_null_checks.rs @@ -38,15 +38,15 @@ fn main() { if (&mut 8 as *mut i32).is_null() {} //~^ WARN references are not nullable if ptr::from_mut(&mut 8).is_null() {} - //~^ WARN references are not nullable + //~^ WARN call is never null if (&8 as *const i32).is_null() {} //~^ WARN references are not nullable if ptr::from_ref(&8).is_null() {} - //~^ WARN references are not nullable + //~^ WARN call is never null if ptr::from_ref(&8).cast_mut().is_null() {} - //~^ WARN references are not nullable + //~^ WARN call is never null if (ptr::from_ref(&8).cast_mut() as *mut i32).is_null() {} - //~^ WARN references are not nullable + //~^ WARN call is never null if (&8 as *const i32) == std::ptr::null() {} //~^ WARN references are not nullable let ref_num = &8; @@ -65,6 +65,12 @@ fn main() { if (&*{ static_i32() } as *const i32).is_null() {} //~^ WARN references are not nullable + // ---------------- Functions ------------------- + if ptr::NonNull::new(&mut 8).unwrap().as_ptr().is_null() {} + //~^ WARN call is never null + if ptr::NonNull::::dangling().as_ptr().is_null() {} + //~^ WARN call is never null + // ---------------------------------------------- const ZPTR: *const () = 0 as *const _; const NOT_ZPTR: *const () = 1 as *const _; diff --git a/tests/ui/lint/ptr_null_checks.stderr b/tests/ui/lint/ptr_null_checks.stderr index 3cee1804b62..0edc1b86536 100644 --- a/tests/ui/lint/ptr_null_checks.stderr +++ b/tests/ui/lint/ptr_null_checks.stderr @@ -117,13 +117,11 @@ LL | if (&mut 8 as *mut i32).is_null() {} | | | expression has type `&mut i32` -warning: references are not nullable, so checking them for null will always return false +warning: returned pointer of `from_mut` call is never null, so checking it for null will always return false --> $DIR/ptr_null_checks.rs:40:8 | LL | if ptr::from_mut(&mut 8).is_null() {} - | ^^^^^^^^^^^^^^------^^^^^^^^^^^ - | | - | expression has type `&mut i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: references are not nullable, so checking them for null will always return false --> $DIR/ptr_null_checks.rs:42:8 @@ -133,29 +131,23 @@ LL | if (&8 as *const i32).is_null() {} | | | expression has type `&i32` -warning: references are not nullable, so checking them for null will always return false +warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false --> $DIR/ptr_null_checks.rs:44:8 | LL | if ptr::from_ref(&8).is_null() {} - | ^^^^^^^^^^^^^^--^^^^^^^^^^^ - | | - | expression has type `&i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: references are not nullable, so checking them for null will always return false +warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false --> $DIR/ptr_null_checks.rs:46:8 | LL | if ptr::from_ref(&8).cast_mut().is_null() {} - | ^^^^^^^^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^ - | | - | expression has type `&i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: references are not nullable, so checking them for null will always return false +warning: returned pointer of `from_ref` call is never null, so checking it for null will always return false --> $DIR/ptr_null_checks.rs:48:8 | LL | if (ptr::from_ref(&8).cast_mut() as *mut i32).is_null() {} - | ^^^^^^^^^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expression has type `&i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: references are not nullable, so checking them for null will always return false --> $DIR/ptr_null_checks.rs:50:8 @@ -221,5 +213,17 @@ LL | if (&*{ static_i32() } as *const i32).is_null() {} | | | expression has type `&i32` -warning: 25 warnings emitted +warning: returned pointer of `as_ptr` call is never null, so checking it for null will always return false + --> $DIR/ptr_null_checks.rs:69:8 + | +LL | if ptr::NonNull::new(&mut 8).unwrap().as_ptr().is_null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: returned pointer of `as_ptr` call is never null, so checking it for null will always return false + --> $DIR/ptr_null_checks.rs:71:8 + | +LL | if ptr::NonNull::::dangling().as_ptr().is_null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 27 warnings emitted From 96b60ed2784a4f9136d63aa581042b8c044f40e5 Mon Sep 17 00:00:00 2001 From: Benedikt Radtke Date: Sun, 6 Aug 2023 23:20:53 +0200 Subject: [PATCH 010/250] stabilize abi_thiscall --- crates/ide-db/src/generated/lints.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/crates/ide-db/src/generated/lints.rs b/crates/ide-db/src/generated/lints.rs index e488300b41c..49b37024a5e 100644 --- a/crates/ide-db/src/generated/lints.rs +++ b/crates/ide-db/src/generated/lints.rs @@ -928,22 +928,6 @@ $ cat $(find -name '*.s') ret; } ``` -"##, - }, - Lint { - label: "abi_thiscall", - description: r##"# `abi_thiscall` - -The tracking issue for this feature is: [#42202] - -[#42202]: https://github.com/rust-lang/rust/issues/42202 - ------------------------- - -The MSVC ABI on x86 Windows uses the `thiscall` calling convention for C++ -instance methods by default; it is identical to the usual (C) calling -convention on x86 Windows except that the first parameter of the method, -the `this` pointer, is passed in the ECX register. "##, }, Lint { From c9bc45f6fd4dfefa3419311f954863c17bb6d575 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Tue, 23 May 2023 15:08:23 -0700 Subject: [PATCH 011/250] feat: `riscv-interrupt-{m,s}` calling conventions Similar to prior support added for the mips430, avr, and x86 targets this change implements the rough equivalent of clang's [`__attribute__((interrupt))`][clang-attr] for riscv targets, enabling e.g. ```rust static mut CNT: usize = 0; pub extern "riscv-interrupt-m" fn isr_m() { unsafe { CNT += 1; } } ``` to produce highly effective assembly like: ```asm pub extern "riscv-interrupt-m" fn isr_m() { 420003a0: 1141 addi sp,sp,-16 unsafe { CNT += 1; 420003a2: c62a sw a0,12(sp) 420003a4: c42e sw a1,8(sp) 420003a6: 3fc80537 lui a0,0x3fc80 420003aa: 63c52583 lw a1,1596(a0) # 3fc8063c <_ZN12esp_riscv_rt3CNT17hcec3e3a214887d53E.0> 420003ae: 0585 addi a1,a1,1 420003b0: 62b52e23 sw a1,1596(a0) } } 420003b4: 4532 lw a0,12(sp) 420003b6: 45a2 lw a1,8(sp) 420003b8: 0141 addi sp,sp,16 420003ba: 30200073 mret ``` (disassembly via `riscv64-unknown-elf-objdump -C -S --disassemble ./esp32c3-hal/target/riscv32imc-unknown-none-elf/release/examples/gpio_interrupt`) This outcome is superior to hand-coded interrupt routines which, lacking visibility into any non-assembly body of the interrupt handler, have to be very conservative and save the [entire CPU state to the stack frame][full-frame-save]. By instead asking LLVM to only save the registers that it uses, we defer the decision to the tool with the best context: it can more accurately account for the cost of spills if it knows that every additional register used is already at the cost of an implicit spill. At the LLVM level, this is apparently [implemented by] marking every register as "[callee-save]," matching the semantics of an interrupt handler nicely (it has to leave the CPU state just as it found it after its `{m|s}ret`). This approach is not suitable for every interrupt handler, as it makes no attempt to e.g. save the state in a user-accessible stack frame. For a full discussion of those challenges and tradeoffs, please refer to [the interrupt calling conventions RFC][rfc]. Inside rustc, this implementation differs from prior art because LLVM does not expose the "all-saved" function flavor as a calling convention directly, instead preferring to use an attribute that allows for differentiating between "machine-mode" and "superivsor-mode" interrupts. Finally, some effort has been made to guide those who may not yet be aware of the differences between machine-mode and supervisor-mode interrupts as to why no `riscv-interrupt` calling convention is exposed through rustc, and similarly for why `riscv-interrupt-u` makes no appearance (as it would complicate future LLVM upgrades). [clang-attr]: https://clang.llvm.org/docs/AttributeReference.html#interrupt-risc-v [full-frame-save]: https://github.com/esp-rs/esp-riscv-rt/blob/9281af2ecffe13e40992917316f36920c26acaf3/src/lib.rs#L440-L469 [implemented by]: https://github.com/llvm/llvm-project/blob/b7fb2a3fec7c187d58a6d338ab512d9173bca987/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp#L61-L67 [callee-save]: https://github.com/llvm/llvm-project/blob/973f1fe7a8591c7af148e573491ab68cc15b6ecf/llvm/lib/Target/RISCV/RISCVCallingConv.td#L30-L37 [rfc]: https://github.com/rust-lang/rfcs/pull/3246 --- crates/ide-completion/src/completions/extern_abi.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/ide-completion/src/completions/extern_abi.rs b/crates/ide-completion/src/completions/extern_abi.rs index c717a9cb55b..e411c1c869c 100644 --- a/crates/ide-completion/src/completions/extern_abi.rs +++ b/crates/ide-completion/src/completions/extern_abi.rs @@ -30,6 +30,8 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[ "efiapi", "avr-interrupt", "avr-non-blocking-interrupt", + "riscv-interrupt-m", + "riscv-interrupt-s", "C-cmse-nonsecure-call", "wasm", "system", From a46eebbc9f0904f35fe992cc44d6a9e64c3e6086 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Wed, 9 Aug 2023 23:51:27 +0200 Subject: [PATCH 012/250] Deunwrap wrap_return_type_in_result --- .../src/handlers/wrap_return_type_in_result.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs index 24c3387457a..51b7181b3d1 100644 --- a/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs +++ b/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs @@ -48,6 +48,12 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< return None; } + let new_result_ty = + make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update(); + let generic_args = + new_result_ty.syntax().descendants().find_map(ast::GenericArgList::cast).unwrap(); + let last_genarg = generic_args.generic_args().last()?; + acc.add( AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite), "Wrap return type in Result", @@ -75,19 +81,12 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< ted::replace(ret_expr_arg.syntax(), ok_wrapped.syntax()); } - let new_result_ty = - make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update(); let old_result_ty = edit.make_mut(type_ref.clone()); ted::replace(old_result_ty.syntax(), new_result_ty.syntax()); if let Some(cap) = ctx.config.snippet_cap { - let generic_args = new_result_ty - .syntax() - .descendants() - .find_map(ast::GenericArgList::cast) - .unwrap(); - edit.add_placeholder_snippet(cap, generic_args.generic_args().last().unwrap()); + edit.add_placeholder_snippet(cap, last_genarg); } }, ) From 93b683815856b765490c203c61e4f3d96fc44d80 Mon Sep 17 00:00:00 2001 From: Dirreke Date: Thu, 13 Jul 2023 22:19:59 +0800 Subject: [PATCH 013/250] add a csky-unknown-linux-gnuabiv2 target --- crates/ide-completion/src/completions/attribute/cfg.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ide-completion/src/completions/attribute/cfg.rs b/crates/ide-completion/src/completions/attribute/cfg.rs index 19bfd294b25..a5af9affb9c 100644 --- a/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/crates/ide-completion/src/completions/attribute/cfg.rs @@ -46,6 +46,7 @@ const KNOWN_ARCH: [&str; 19] = [ "aarch64", "arm", "avr", + "csky", "hexagon", "mips", "mips64", From 465aaed1cf7cd271cc5532a2e0e7371ad028626b Mon Sep 17 00:00:00 2001 From: dirreke Date: Tue, 25 Jul 2023 20:54:44 +0800 Subject: [PATCH 014/250] fix the wrong number in const KNOWN_ARCH --- crates/ide-completion/src/completions/attribute/cfg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide-completion/src/completions/attribute/cfg.rs b/crates/ide-completion/src/completions/attribute/cfg.rs index a5af9affb9c..62bdb6ee688 100644 --- a/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/crates/ide-completion/src/completions/attribute/cfg.rs @@ -42,7 +42,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { }; } -const KNOWN_ARCH: [&str; 19] = [ +const KNOWN_ARCH: [&str; 20] = [ "aarch64", "arm", "avr", From d6b97e64fc561c72138c9aba5f89d0c3c8ca6f86 Mon Sep 17 00:00:00 2001 From: dirreke Date: Mon, 14 Aug 2023 22:57:38 +0800 Subject: [PATCH 015/250] Upgrade Object and related deps --- crates/proc-macro-api/Cargo.toml | 2 +- crates/proc-macro-srv/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/proc-macro-api/Cargo.toml b/crates/proc-macro-api/Cargo.toml index 4e39167136b..4229f289130 100644 --- a/crates/proc-macro-api/Cargo.toml +++ b/crates/proc-macro-api/Cargo.toml @@ -12,7 +12,7 @@ rust-version.workspace = true doctest = false [dependencies] -object = { version = "0.31.0", default-features = false, features = [ +object = { version = "0.32.0", default-features = false, features = [ "std", "read_core", "elf", diff --git a/crates/proc-macro-srv/Cargo.toml b/crates/proc-macro-srv/Cargo.toml index ecc6aaa0ac7..99993f16e27 100644 --- a/crates/proc-macro-srv/Cargo.toml +++ b/crates/proc-macro-srv/Cargo.toml @@ -12,7 +12,7 @@ rust-version.workspace = true doctest = false [dependencies] -object = { version = "0.31.0", default-features = false, features = [ +object = { version = "0.32.0", default-features = false, features = [ "std", "read_core", "elf", From 4ddc6a780eb0be042eb12f7c50b23d306d786de7 Mon Sep 17 00:00:00 2001 From: dirreke Date: Tue, 15 Aug 2023 01:27:26 +0800 Subject: [PATCH 016/250] update Cargo.lock --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8806794979..a2b263cf2d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1196,9 +1196,9 @@ dependencies = [ [[package]] name = "object" -version = "0.31.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" dependencies = [ "memchr", ] @@ -1337,7 +1337,7 @@ name = "proc-macro-api" version = "0.0.0" dependencies = [ "memmap2", - "object 0.31.1", + "object 0.32.0", "paths", "profile", "serde", @@ -1357,7 +1357,7 @@ dependencies = [ "libloading", "mbe", "memmap2", - "object 0.31.1", + "object 0.32.0", "paths", "proc-macro-api", "proc-macro-test", From eed1b3b6524ffd63cce76a72ef14e9bd83e7c65a Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Tue, 15 Aug 2023 19:37:23 +0200 Subject: [PATCH 017/250] v2 --- crates/ide-assists/src/handlers/wrap_return_type_in_result.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs index 51b7181b3d1..61e9bcdcc51 100644 --- a/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs +++ b/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs @@ -50,8 +50,7 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< let new_result_ty = make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update(); - let generic_args = - new_result_ty.syntax().descendants().find_map(ast::GenericArgList::cast).unwrap(); + let generic_args = new_result_ty.syntax().descendants().find_map(ast::GenericArgList::cast)?; let last_genarg = generic_args.generic_args().last()?; acc.add( From 14078ed303c80bc6d4b1b5f441bbb1e04a1f3adf Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Tue, 15 Aug 2023 15:58:21 -0700 Subject: [PATCH 018/250] Rename CargoHandle to CommandHandle This handle wraps an arbitrary command, which might be a rustc command rather than cargo. --- crates/flycheck/src/lib.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index fbb943ccb99..7c52eb0b5be 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -168,7 +168,7 @@ struct FlycheckActor { /// doesn't provide a way to read sub-process output without blocking, so we /// have to wrap sub-processes output handling in a thread and pass messages /// back over a channel. - cargo_handle: Option, + command_handle: Option, } enum Event { @@ -184,7 +184,7 @@ impl FlycheckActor { workspace_root: AbsPathBuf, ) -> FlycheckActor { tracing::info!(%id, ?workspace_root, "Spawning flycheck"); - FlycheckActor { id, sender, config, root: workspace_root, cargo_handle: None } + FlycheckActor { id, sender, config, root: workspace_root, command_handle: None } } fn report_progress(&self, progress: Progress) { @@ -192,7 +192,7 @@ impl FlycheckActor { } fn next_event(&self, inbox: &Receiver) -> Option { - let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver); + let check_chan = self.command_handle.as_ref().map(|cargo| &cargo.receiver); if let Ok(msg) = inbox.try_recv() { // give restarts a preference so check outputs don't block a restart or stop return Some(Event::RequestStateChange(msg)); @@ -222,13 +222,13 @@ impl FlycheckActor { let command = self.check_command(); tracing::debug!(?command, "will restart flycheck"); - match CargoHandle::spawn(command) { - Ok(cargo_handle) => { + match CommandHandle::spawn(command) { + Ok(command_handle) => { tracing::debug!( command = ?self.check_command(), "did restart flycheck" ); - self.cargo_handle = Some(cargo_handle); + self.command_handle = Some(command_handle); self.report_progress(Progress::DidStart); } Err(error) => { @@ -244,8 +244,8 @@ impl FlycheckActor { tracing::debug!(flycheck_id = self.id, "flycheck finished"); // Watcher finished - let cargo_handle = self.cargo_handle.take().unwrap(); - let res = cargo_handle.join(); + let command_handle = self.command_handle.take().unwrap(); + let res = command_handle.join(); if res.is_err() { tracing::error!( "Flycheck failed to run the following command: {:?}", @@ -284,12 +284,12 @@ impl FlycheckActor { } fn cancel_check_process(&mut self) { - if let Some(cargo_handle) = self.cargo_handle.take() { + if let Some(command_handle) = self.command_handle.take() { tracing::debug!( command = ?self.check_command(), "did cancel flycheck" ); - cargo_handle.cancel(); + command_handle.cancel(); self.report_progress(Progress::DidCancel); } } @@ -391,7 +391,7 @@ impl Drop for JodGroupChild { } /// A handle to a cargo process used for fly-checking. -struct CargoHandle { +struct CommandHandle { /// The handle to the actual cargo process. As we cannot cancel directly from with /// a read syscall dropping and therefore terminating the process is our best option. child: JodGroupChild, @@ -399,8 +399,8 @@ struct CargoHandle { receiver: Receiver, } -impl CargoHandle { - fn spawn(mut command: Command) -> std::io::Result { +impl CommandHandle { + fn spawn(mut command: Command) -> std::io::Result { command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); let mut child = command.group_spawn().map(JodGroupChild)?; @@ -413,7 +413,7 @@ impl CargoHandle { .name("CargoHandle".to_owned()) .spawn(move || actor.run()) .expect("failed to spawn thread"); - Ok(CargoHandle { child, thread, receiver }) + Ok(CommandHandle { child, thread, receiver }) } fn cancel(mut self) { From e2866404ef854641502974b400769518f8c203d2 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Tue, 15 Aug 2023 16:02:55 -0700 Subject: [PATCH 019/250] Format the existing command in logging --- crates/flycheck/src/lib.rs | 41 ++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 7c52eb0b5be..2de719af92c 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -5,7 +5,9 @@ #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] use std::{ + ffi::OsString, fmt, io, + path::PathBuf, process::{ChildStderr, ChildStdout, Command, Stdio}, time::Duration, }; @@ -221,21 +223,19 @@ impl FlycheckActor { } let command = self.check_command(); + let formatted_command = format!("{:?}", command); + tracing::debug!(?command, "will restart flycheck"); match CommandHandle::spawn(command) { Ok(command_handle) => { - tracing::debug!( - command = ?self.check_command(), - "did restart flycheck" - ); + tracing::debug!(command = formatted_command, "did restart flycheck"); self.command_handle = Some(command_handle); self.report_progress(Progress::DidStart); } Err(error) => { self.report_progress(Progress::DidFailToRestart(format!( - "Failed to run the following command: {:?} error={}", - self.check_command(), - error + "Failed to run the following command: {} error={}", + formatted_command, error ))); } } @@ -245,11 +245,13 @@ impl FlycheckActor { // Watcher finished let command_handle = self.command_handle.take().unwrap(); + let formatted_handle = format!("{:?}", command_handle); + let res = command_handle.join(); if res.is_err() { tracing::error!( - "Flycheck failed to run the following command: {:?}", - self.check_command() + "Flycheck failed to run the following command: {}", + formatted_handle ); } self.report_progress(Progress::DidFinish(res)); @@ -286,7 +288,7 @@ impl FlycheckActor { fn cancel_check_process(&mut self) { if let Some(command_handle) = self.command_handle.take() { tracing::debug!( - command = ?self.check_command(), + command = ?command_handle, "did cancel flycheck" ); command_handle.cancel(); @@ -397,6 +399,19 @@ struct CommandHandle { child: JodGroupChild, thread: stdx::thread::JoinHandle>, receiver: Receiver, + program: OsString, + arguments: Vec, + current_dir: Option, +} + +impl fmt::Debug for CommandHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CommandHandle") + .field("program", &self.program) + .field("arguments", &self.arguments) + .field("current_dir", &self.current_dir) + .finish() + } } impl CommandHandle { @@ -404,6 +419,10 @@ impl CommandHandle { command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); let mut child = command.group_spawn().map(JodGroupChild)?; + let program = command.get_program().into(); + let arguments = command.get_args().map(|arg| arg.into()).collect::>(); + let current_dir = command.get_current_dir().map(|arg| arg.to_path_buf()); + let stdout = child.0.inner().stdout.take().unwrap(); let stderr = child.0.inner().stderr.take().unwrap(); @@ -413,7 +432,7 @@ impl CommandHandle { .name("CargoHandle".to_owned()) .spawn(move || actor.run()) .expect("failed to spawn thread"); - Ok(CommandHandle { child, thread, receiver }) + Ok(CommandHandle { program, arguments, current_dir, child, thread, receiver }) } fn cancel(mut self) { From a38ea96825ae9ea66812445c9a953aeb95403702 Mon Sep 17 00:00:00 2001 From: Ben Schulz Date: Fri, 18 Aug 2023 14:05:55 +0200 Subject: [PATCH 020/250] Remove Drop impl of mpsc Receiver and (Sync)Sender --- library/std/src/sync/mpsc/mod.rs | 15 --------------- tests/ui/span/send-is-not-static-std-sync.rs | 2 +- tests/ui/span/send-is-not-static-std-sync.stderr | 8 +++----- 3 files changed, 4 insertions(+), 21 deletions(-) diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index f92bb1a4b1f..d353c7bd5de 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -626,11 +626,6 @@ impl Clone for Sender { } } -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for Sender { - fn drop(&mut self) {} -} - #[stable(feature = "mpsc_debug", since = "1.8.0")] impl fmt::Debug for Sender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -755,11 +750,6 @@ impl Clone for SyncSender { } } -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for SyncSender { - fn drop(&mut self) {} -} - #[stable(feature = "mpsc_debug", since = "1.8.0")] impl fmt::Debug for SyncSender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1096,11 +1086,6 @@ impl IntoIterator for Receiver { } } -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for Receiver { - fn drop(&mut self) {} -} - #[stable(feature = "mpsc_debug", since = "1.8.0")] impl fmt::Debug for Receiver { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/tests/ui/span/send-is-not-static-std-sync.rs b/tests/ui/span/send-is-not-static-std-sync.rs index f8ab5243c22..9c1ee287154 100644 --- a/tests/ui/span/send-is-not-static-std-sync.rs +++ b/tests/ui/span/send-is-not-static-std-sync.rs @@ -46,7 +46,7 @@ fn channel() { tx.send(&z).unwrap(); } //~^^ ERROR `z` does not live long enough - // (channels lack #[may_dangle], thus their dtors are implicit uses of `z`) + tx.use_ref(); // (channel drop glue does not use `z` => needs explicit use) } fn main() {} diff --git a/tests/ui/span/send-is-not-static-std-sync.stderr b/tests/ui/span/send-is-not-static-std-sync.stderr index eaba415adaa..46534b39168 100644 --- a/tests/ui/span/send-is-not-static-std-sync.stderr +++ b/tests/ui/span/send-is-not-static-std-sync.stderr @@ -75,11 +75,9 @@ LL | tx.send(&z).unwrap(); | ^^ borrowed value does not live long enough LL | } | - `z` dropped here while still borrowed -... -LL | } - | - borrow might be used here, when `tx` is dropped and runs the `Drop` code for type `Sender` - | - = note: values in a scope are dropped in the opposite order they are defined +LL | +LL | tx.use_ref(); // (channel drop glue does not use `z` => needs explicit use) + | -- borrow later used here error: aborting due to 6 previous errors From 59f9c95ec070b3e249275078f327148f341a4960 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Aug 2023 13:10:25 +0200 Subject: [PATCH 021/250] give some unwind-related terminators a more clear name --- crates/hir-ty/src/mir.rs | 2 +- crates/hir-ty/src/mir/borrowck.rs | 6 +++--- crates/hir-ty/src/mir/monomorphization.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index 4723c25ed08..76a535e3067 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -1057,7 +1057,7 @@ impl MirBody { TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::GeneratorDrop | TerminatorKind::Abort | TerminatorKind::Return diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index ad98e8fa181..c70d7f63fd8 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -160,7 +160,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::GeneratorDrop | TerminatorKind::Abort | TerminatorKind::Return @@ -280,7 +280,7 @@ fn ever_initialized_map( let targets = match &terminator.kind { TerminatorKind::Goto { target } => vec![*target], TerminatorKind::SwitchInt { targets, .. } => targets.all_targets().to_vec(), - TerminatorKind::Resume + TerminatorKind::UnwindResume | TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::Unreachable => vec![], @@ -371,7 +371,7 @@ fn mutability_of_locals( }; match &terminator.kind { TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::Unreachable diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs index c565228d91e..df16d0d8201 100644 --- a/crates/hir-ty/src/mir/monomorphization.rs +++ b/crates/hir-ty/src/mir/monomorphization.rs @@ -265,7 +265,7 @@ impl Filler<'_> { self.fill_operand(discr)?; } TerminatorKind::Goto { .. } - | TerminatorKind::Resume + | TerminatorKind::UnwindResume | TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::Unreachable From 883f16d8057a9f1f43f34cac9ebdf90e58f7ebbd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 20 Aug 2023 18:31:22 +0200 Subject: [PATCH 022/250] fix RA build --- crates/hir-ty/src/mir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index 76a535e3067..9be083d0117 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -368,7 +368,7 @@ pub enum TerminatorKind { /// /// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after /// deaggregation runs. - Resume, + UnwindResume, /// Indicates that the landing pad is finished and that the process should abort. /// From 30d8aa1becc4c06f4326812e4bab997768a86c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 21 Aug 2023 12:44:09 +0300 Subject: [PATCH 023/250] Merge commit '9b3d03408c66749d56466bb09baf2a7177deb6ce' into sync-from-ra --- Cargo.lock | 12 +- Cargo.toml | 5 +- crates/base-db/src/fixture.rs | 4 +- crates/base-db/src/input.rs | 6 + crates/hir-def/src/attr.rs | 10 +- crates/hir-def/src/body/lower.rs | 7 +- crates/hir-def/src/body/tests/block.rs | 20 +- crates/hir-def/src/data.rs | 2 +- crates/hir-def/src/db.rs | 3 + crates/hir-def/src/find_path.rs | 81 ++- crates/hir-def/src/generics.rs | 85 +++- crates/hir-def/src/hir/type_ref.rs | 11 + crates/hir-def/src/import_map.rs | 42 +- crates/hir-def/src/item_scope.rs | 481 ++++++++++++++---- crates/hir-def/src/item_tree.rs | 51 +- crates/hir-def/src/item_tree/lower.rs | 21 +- crates/hir-def/src/item_tree/pretty.rs | 27 +- crates/hir-def/src/item_tree/tests.rs | 12 + crates/hir-def/src/lib.rs | 44 +- .../macro_expansion_tests/builtin_fn_macro.rs | 2 +- .../macro_expansion_tests/mbe/regression.rs | 65 +++ .../hir-def/src/macro_expansion_tests/mod.rs | 2 +- crates/hir-def/src/nameres.rs | 72 +-- crates/hir-def/src/nameres/collector.rs | 438 ++++++++-------- crates/hir-def/src/nameres/path_resolution.rs | 85 ++-- crates/hir-def/src/nameres/tests.rs | 80 +-- crates/hir-def/src/nameres/tests/globs.rs | 18 +- .../hir-def/src/nameres/tests/incremental.rs | 2 +- crates/hir-def/src/nameres/tests/macros.rs | 52 +- .../src/nameres/tests/mod_resolution.rs | 32 +- .../hir-def/src/nameres/tests/primitives.rs | 4 +- crates/hir-def/src/per_ns.rs | 82 ++- crates/hir-def/src/resolver.rs | 189 +++---- crates/hir-def/src/src.rs | 19 +- crates/hir-expand/src/attrs.rs | 9 +- crates/hir-expand/src/lib.rs | 19 +- crates/hir-ty/src/builder.rs | 13 +- crates/hir-ty/src/consteval.rs | 4 +- crates/hir-ty/src/consteval/tests.rs | 53 +- crates/hir-ty/src/diagnostics/unsafe_check.rs | 2 +- crates/hir-ty/src/display.rs | 42 +- crates/hir-ty/src/infer.rs | 8 +- crates/hir-ty/src/infer/closure.rs | 2 +- crates/hir-ty/src/infer/expr.rs | 21 +- crates/hir-ty/src/infer/path.rs | 4 +- crates/hir-ty/src/infer/unify.rs | 48 +- crates/hir-ty/src/lib.rs | 17 +- crates/hir-ty/src/lower.rs | 123 ++--- crates/hir-ty/src/mir/eval.rs | 31 +- crates/hir-ty/src/mir/eval/shim.rs | 102 +++- crates/hir-ty/src/mir/eval/tests.rs | 42 ++ crates/hir-ty/src/mir/lower.rs | 52 +- .../hir-ty/src/mir/lower/pattern_matching.rs | 2 +- crates/hir/src/attrs.rs | 183 +++++-- crates/hir/src/display.rs | 68 ++- crates/hir/src/lib.rs | 68 ++- crates/hir/src/semantics.rs | 91 +++- crates/hir/src/source_analyzer.rs | 9 +- crates/hir/src/symbols.rs | 40 +- .../src/handlers/add_missing_impl_members.rs | 105 +++- .../src/handlers/apply_demorgan.rs | 188 ++++--- .../convert_named_struct_to_tuple_struct.rs | 4 +- .../src/handlers/convert_to_guarded_return.rs | 21 +- .../extract_expressions_from_format_string.rs | 2 +- .../src/handlers/extract_function.rs | 123 +++-- .../src/handlers/generate_delegate_methods.rs | 9 +- .../src/handlers/generate_derive.rs | 37 +- crates/ide-assists/src/handlers/remove_dbg.rs | 5 +- .../src/handlers/remove_unused_imports.rs | 2 +- crates/ide-completion/src/completions.rs | 6 +- .../src/completions/extern_crate.rs | 71 +++ crates/ide-completion/src/completions/type.rs | 85 ++-- crates/ide-completion/src/context.rs | 53 +- crates/ide-completion/src/context/analysis.rs | 145 +++++- crates/ide-completion/src/tests/flyimport.rs | 54 ++ crates/ide-completion/src/tests/type_pos.rs | 294 ++++++++++- crates/ide-db/src/defs.rs | 12 +- crates/ide-db/src/helpers.rs | 2 +- crates/ide-db/src/imports/import_assets.rs | 4 +- crates/ide-db/src/lib.rs | 3 + crates/ide-db/src/path_transform.rs | 44 +- crates/ide-db/src/search.rs | 4 +- crates/ide-db/src/symbol_index.rs | 11 +- .../test_symbol_index_collection.txt | 203 ++++++++ crates/ide-db/src/use_trivial_constructor.rs | 16 +- crates/ide-ssr/src/matching.rs | 6 +- crates/ide/src/call_hierarchy.rs | 10 +- crates/ide/src/doc_links.rs | 10 +- crates/ide/src/doc_links/tests.rs | 56 ++ crates/ide/src/expand_macro.rs | 47 +- crates/ide/src/extend_selection.rs | 29 +- crates/ide/src/goto_declaration.rs | 8 +- crates/ide/src/goto_definition.rs | 54 +- crates/ide/src/goto_implementation.rs | 15 +- crates/ide/src/goto_type_definition.rs | 8 +- crates/ide/src/highlight_related.rs | 17 +- crates/ide/src/hover.rs | 4 +- crates/ide/src/hover/tests.rs | 107 +++- crates/ide/src/lib.rs | 2 +- crates/ide/src/moniker.rs | 2 +- crates/ide/src/navigation_target.rs | 8 +- crates/ide/src/references.rs | 2 +- crates/ide/src/signature_help.rs | 32 +- crates/ide/src/syntax_highlighting.rs | 4 +- crates/mbe/src/syntax_bridge.rs | 1 + crates/parser/src/grammar.rs | 4 +- crates/parser/src/grammar/generic_params.rs | 2 +- crates/parser/src/shortcuts.rs | 13 +- ...22_recover_from_missing_const_default.rast | 6 +- .../ok/0188_const_param_default_path.rast | 15 +- .../0199_const_param_default_expression.rast | 17 +- .../ok/0200_const_param_default_literal.rast | 9 +- crates/profile/src/stop_watch.rs | 20 +- crates/project-model/src/workspace.rs | 3 +- crates/rust-analyzer/Cargo.toml | 2 +- .../rust-analyzer/src/cli/analysis_stats.rs | 8 +- crates/rust-analyzer/src/cli/flags.rs | 3 - crates/rust-analyzer/src/config.rs | 13 +- crates/rust-analyzer/src/diagnostics.rs | 2 + .../rust-analyzer/src/diagnostics/to_proto.rs | 7 + crates/syntax/rust.ungram | 2 +- crates/syntax/src/ast/generated/nodes.rs | 2 +- crates/syntax/src/ast/make.rs | 7 +- crates/syntax/src/ast/node_ext.rs | 8 + crates/syntax/src/lib.rs | 12 +- docs/user/generated_config.adoc | 15 +- editors/code/package.json | 18 +- editors/code/src/bootstrap.ts | 2 +- editors/code/src/commands.ts | 7 + editors/code/src/config.ts | 33 ++ editors/code/src/ctx.ts | 18 +- editors/code/src/main.ts | 1 + editors/code/src/util.ts | 9 +- lib/lsp-server/Cargo.toml | 5 +- lib/lsp-server/src/stdio.rs | 5 +- xtask/src/metrics.rs | 4 +- 136 files changed, 3865 insertions(+), 1451 deletions(-) create mode 100644 crates/ide-completion/src/completions/extern_crate.rs diff --git a/Cargo.lock b/Cargo.lock index a2b263cf2d8..49e96236c51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -999,23 +999,23 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "lsp-server" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3711e4d6f491dc9edc0f1df80e204f38206775ac92c1241e89b79229a850bc00" +version = "0.7.3" dependencies = [ "crossbeam-channel", "log", + "lsp-types", "serde", "serde_json", ] [[package]] name = "lsp-server" -version = "0.7.2" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72417faa455bfb4e5bf14b157d8e2ca2ed74b4e89b8cf42ea2d864825ae5c8a2" dependencies = [ "crossbeam-channel", "log", - "lsp-types", "serde", "serde_json", ] @@ -1555,7 +1555,7 @@ dependencies = [ "ide-ssr", "itertools", "load-cargo", - "lsp-server 0.7.1", + "lsp-server 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", "mbe", "mimalloc", diff --git a/Cargo.toml b/Cargo.toml index f6a50bfa6b2..5eb59d6db11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,7 @@ proc-macro-test = { path = "./crates/proc-macro-test" } # In-tree crates that are published separately and follow semver. See lib/README.md line-index = { version = "0.1.0-pre.1" } la-arena = { version = "0.3.1" } -lsp-server = { version = "0.7.1" } +lsp-server = { version = "0.7.3" } # non-local crates smallvec = { version = "1.10.0", features = [ @@ -97,7 +97,8 @@ smallvec = { version = "1.10.0", features = [ smol_str = "0.2.0" nohash-hasher = "0.2.0" text-size = "1.1.0" -serde = { version = "1.0.156", features = ["derive"] } +# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde +serde = { version = "1.0.156, < 1.0.172", features = ["derive"] } serde_json = "1.0.96" triomphe = { version = "0.1.8", default-features = false, features = ["std"] } # can't upgrade due to dashmap depending on 0.12.3 currently diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs index 323ee4260e4..aaac0fc3790 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/base-db/src/fixture.rs @@ -130,6 +130,7 @@ impl ChangeFixture { let mut default_crate_root: Option = None; let mut default_target_data_layout: Option = None; let mut default_cfg = CfgOptions::default(); + let mut default_env = Env::new_for_test_fixture(); let mut file_set = FileSet::default(); let mut current_source_root_kind = SourceRootKind::Local; @@ -200,6 +201,7 @@ impl ChangeFixture { assert!(default_crate_root.is_none()); default_crate_root = Some(file_id); default_cfg = meta.cfg; + default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned()))); default_target_data_layout = meta.target_data_layout; } @@ -220,7 +222,7 @@ impl ChangeFixture { None, default_cfg, Default::default(), - Env::new_for_test_fixture(), + default_env, false, CrateOrigin::Local { repo: None, name: None }, default_target_data_layout diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index c47799f1320..b75c7079be7 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -686,6 +686,12 @@ impl fmt::Display for Edition { } } +impl Extend<(String, String)> for Env { + fn extend>(&mut self, iter: T) { + self.entries.extend(iter); + } +} + impl FromIterator<(String, String)> for Env { fn from_iter>(iter: T) -> Self { Env { entries: FromIterator::from_iter(iter) } diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index fae07111806..a5db75a91eb 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -431,12 +431,10 @@ impl AttrsWithOwner { .item_tree(db) .raw_attrs(AttrOwner::ModItem(definition_tree_id.value.into())) .clone(), - ModuleOrigin::BlockExpr { block } => RawAttrs::from_attrs_owner( - db.upcast(), - InFile::new(block.file_id, block.to_node(db.upcast())) - .as_ref() - .map(|it| it as &dyn ast::HasAttrs), - ), + ModuleOrigin::BlockExpr { id, .. } => { + let tree = db.block_item_tree_query(id); + tree.raw_attrs(AttrOwner::TopLevel).clone() + } } } AttrDefId::FieldId(it) => { diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 3853a6ab3a5..7071fcb9394 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -505,6 +505,9 @@ impl ExprCollector<'_> { let mut args = Vec::new(); let mut arg_types = Vec::new(); if let Some(pl) = e.param_list() { + let num_params = pl.params().count(); + args.reserve_exact(num_params); + arg_types.reserve_exact(num_params); for param in pl.params() { let pat = this.collect_pat_top(param.pat()); let type_ref = @@ -1100,7 +1103,9 @@ impl ExprCollector<'_> { ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))), _ => false, }); - statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_))) + statement_has_item + || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_))) + || (block.may_carry_attributes() && block.attrs().next().is_some()) }; let block_id = if block_has_items { diff --git a/crates/hir-def/src/body/tests/block.rs b/crates/hir-def/src/body/tests/block.rs index 4e015a7fbbb..44eeed9e3fb 100644 --- a/crates/hir-def/src/body/tests/block.rs +++ b/crates/hir-def/src/body/tests/block.rs @@ -38,9 +38,9 @@ fn outer() { "#, expect![[r#" block scope - CrateStruct: t - PlainStruct: t v - SelfStruct: t + CrateStruct: ti + PlainStruct: ti vi + SelfStruct: ti Struct: v SuperStruct: _ @@ -66,7 +66,7 @@ fn outer() { "#, expect![[r#" block scope - imported: t v + imported: ti vi name: v crate @@ -92,9 +92,9 @@ fn outer() { "#, expect![[r#" block scope - inner1: t + inner1: ti inner2: v - outer: v + outer: vi block scope inner: v @@ -121,7 +121,7 @@ struct Struct {} "#, expect![[r#" block scope - Struct: t + Struct: ti crate Struct: t @@ -153,7 +153,7 @@ fn outer() { "#, expect![[r#" block scope - ResolveMe: t + ResolveMe: ti block scope m2: t @@ -214,7 +214,7 @@ fn f() { "#, expect![[r#" block scope - ResolveMe: t + ResolveMe: ti block scope h: v @@ -292,7 +292,7 @@ pub mod cov_mark { nested: v crate - cov_mark: t + cov_mark: ti f: v "#]], ); diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index 91db68058b0..68defa3858f 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -487,7 +487,7 @@ impl ExternCrateDeclData { db.crate_def_map(loc.container.krate()) .extern_prelude() .find(|&(prelude_name, ..)| *prelude_name == name) - .map(|(_, root)| root.krate()) + .map(|(_, (root, _))| root.krate()) }; Arc::new(Self { diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index e34a6768f28..31c1a713031 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -82,6 +82,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Arc; + #[salsa::invoke(ItemTree::block_item_tree_query)] + fn block_item_tree_query(&self, block_id: BlockId) -> Arc; + #[salsa::invoke(crate_def_map_wait)] #[salsa::transparent] fn crate_def_map(&self, krate: CrateId) -> Arc; diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index df2af4c89b0..b60e7909105 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -11,7 +11,7 @@ use crate::{ nameres::DefMap, path::{ModPath, PathKind}, visibility::Visibility, - ModuleDefId, ModuleId, + CrateRootModuleId, ModuleDefId, ModuleId, }; /// Find a path that can be used to refer to a certain item. This can depend on @@ -81,7 +81,7 @@ fn find_path_inner( } let def_map = from.def_map(db); - let crate_root = def_map.crate_root().into(); + let crate_root = def_map.crate_root(); // - if the item is a module, jump straight to module search if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item { let mut visited_modules = FxHashSet::default(); @@ -149,7 +149,7 @@ fn find_path_for_module( db: &dyn DefDatabase, def_map: &DefMap, visited_modules: &mut FxHashSet, - crate_root: ModuleId, + crate_root: CrateRootModuleId, from: ModuleId, module_id: ModuleId, max_len: usize, @@ -183,7 +183,7 @@ fn find_path_for_module( // - if the item is the crate root of a dependency crate, return the name from the extern prelude let root_def_map = crate_root.def_map(db); - for (name, def_id) in root_def_map.extern_prelude() { + for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude() { if module_id == def_id { let name = scope_name.unwrap_or_else(|| name.clone()); @@ -192,7 +192,7 @@ fn find_path_for_module( def_map[local_id] .scope .type_(&name) - .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id)) + .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id.into())) }) .is_some(); let kind = if name_already_occupied_in_type_ns { @@ -224,6 +224,7 @@ fn find_path_for_module( ) } +// FIXME: Do we still need this now that we record import origins, and hence aliases? fn find_in_scope( db: &dyn DefDatabase, def_map: &DefMap, @@ -244,7 +245,7 @@ fn find_in_prelude( item: ItemInNs, from: ModuleId, ) -> Option { - let prelude_module = root_def_map.prelude()?; + let (prelude_module, _) = root_def_map.prelude()?; // Preludes in block DefMaps are ignored, only the crate DefMap is searched let prelude_def_map = prelude_module.def_map(db); let prelude_scope = &prelude_def_map[prelude_module.local_id].scope; @@ -293,7 +294,7 @@ fn calculate_best_path( db: &dyn DefDatabase, def_map: &DefMap, visited_modules: &mut FxHashSet, - crate_root: ModuleId, + crate_root: CrateRootModuleId, max_len: usize, item: ItemInNs, from: ModuleId, @@ -346,6 +347,11 @@ fn calculate_best_path( let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { let import_map = db.import_map(dep.crate_id); import_map.import_info_for(item).and_then(|info| { + if info.is_doc_hidden { + // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate + return None; + } + // Determine best path for containing module and append last segment from `info`. // FIXME: we should guide this to look up the path locally, or from the same crate again? let mut path = find_path_for_module( @@ -1293,4 +1299,65 @@ pub mod prelude { "None", ); } + + #[test] + fn different_crate_renamed_through_dep() { + check_found_path( + r#" +//- /main.rs crate:main deps:intermediate +$0 +//- /intermediate.rs crate:intermediate deps:std +pub extern crate std as std_renamed; +//- /std.rs crate:std +pub struct S; + "#, + "intermediate::std_renamed::S", + "intermediate::std_renamed::S", + "intermediate::std_renamed::S", + "intermediate::std_renamed::S", + ); + } + + #[test] + fn different_crate_doc_hidden() { + check_found_path( + r#" +//- /main.rs crate:main deps:intermediate +$0 +//- /intermediate.rs crate:intermediate deps:std +#[doc(hidden)] +pub extern crate std; +pub extern crate std as longer; +//- /std.rs crate:std +pub struct S; + "#, + "intermediate::longer::S", + "intermediate::longer::S", + "intermediate::longer::S", + "intermediate::longer::S", + ); + } + + #[test] + fn respect_doc_hidden() { + check_found_path( + r#" +//- /main.rs crate:main deps:std,lazy_static +$0 +//- /lazy_static.rs crate:lazy_static deps:core +#[doc(hidden)] +pub use core::ops::Deref as __Deref; +//- /std.rs crate:std deps:core +pub use core::ops; +//- /core.rs crate:core +pub mod ops { + pub trait Deref {} +} + "#, + "std::ops::Deref", + "std::ops::Deref", + "std::ops::Deref", + "std::ops::Deref", + ); + } } diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs index d7d44e41388..1e2535a8a99 100644 --- a/crates/hir-def/src/generics.rs +++ b/crates/hir-def/src/generics.rs @@ -21,10 +21,11 @@ use crate::{ db::DefDatabase, dyn_map::{keys, DynMap}, expander::Expander, + item_tree::{AttrOwner, ItemTree}, lower::LowerCtx, nameres::{DefMap, MacroSubNs}, src::{HasChildSource, HasSource}, - type_ref::{LifetimeRef, TypeBound, TypeRef}, + type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef}, AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId, }; @@ -48,7 +49,7 @@ pub struct LifetimeParamData { pub struct ConstParamData { pub name: Name, pub ty: Interned, - pub has_default: bool, + pub default: Option, } #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] @@ -75,7 +76,7 @@ impl TypeOrConstParamData { pub fn has_default(&self) -> bool { match self { TypeOrConstParamData::TypeParamData(it) => it.default.is_some(), - TypeOrConstParamData::ConstParamData(it) => it.has_default, + TypeOrConstParamData::ConstParamData(it) => it.default.is_some(), } } @@ -154,12 +155,58 @@ impl GenericParams { def: GenericDefId, ) -> Interned { let _p = profile::span("generic_params_query"); + + let krate = def.module(db).krate; + let cfg_options = db.crate_graph(); + let cfg_options = &cfg_options[krate].cfg_options; + + // Returns the generic parameters that are enabled under the current `#[cfg]` options + let enabled_params = |params: &Interned, item_tree: &ItemTree| { + let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options); + + // In the common case, no parameters will by disabled by `#[cfg]` attributes. + // Therefore, make a first pass to check if all parameters are enabled and, if so, + // clone the `Interned` instead of recreating an identical copy. + let all_type_or_consts_enabled = + params.type_or_consts.iter().all(|(idx, _)| enabled(idx.into())); + let all_lifetimes_enabled = params.lifetimes.iter().all(|(idx, _)| enabled(idx.into())); + + if all_type_or_consts_enabled && all_lifetimes_enabled { + params.clone() + } else { + Interned::new(GenericParams { + type_or_consts: all_type_or_consts_enabled + .then(|| params.type_or_consts.clone()) + .unwrap_or_else(|| { + params + .type_or_consts + .iter() + .filter_map(|(idx, param)| { + enabled(idx.into()).then(|| param.clone()) + }) + .collect() + }), + lifetimes: all_lifetimes_enabled + .then(|| params.lifetimes.clone()) + .unwrap_or_else(|| { + params + .lifetimes + .iter() + .filter_map(|(idx, param)| { + enabled(idx.into()).then(|| param.clone()) + }) + .collect() + }), + where_predicates: params.where_predicates.clone(), + }) + } + }; macro_rules! id_to_generics { ($id:ident) => {{ let id = $id.lookup(db).id; let tree = id.item_tree(db); let item = &tree[id.value]; - item.generic_params.clone() + enabled_params(&item.generic_params, &tree) }}; } @@ -169,7 +216,8 @@ impl GenericParams { let tree = loc.id.item_tree(db); let item = &tree[loc.id.value]; - let mut generic_params = GenericParams::clone(&item.explicit_generic_params); + let enabled_params = enabled_params(&item.explicit_generic_params, &tree); + let mut generic_params = GenericParams::clone(&enabled_params); let module = loc.container.module(db); let func_data = db.function_data(id); @@ -198,9 +246,14 @@ impl GenericParams { } } - pub(crate) fn fill(&mut self, lower_ctx: &LowerCtx<'_>, node: &dyn HasGenericParams) { + pub(crate) fn fill( + &mut self, + lower_ctx: &LowerCtx<'_>, + node: &dyn HasGenericParams, + add_param_attrs: impl FnMut(AttrOwner, ast::GenericParam), + ) { if let Some(params) = node.generic_param_list() { - self.fill_params(lower_ctx, params) + self.fill_params(lower_ctx, params, add_param_attrs) } if let Some(where_clause) = node.where_clause() { self.fill_where_predicates(lower_ctx, where_clause); @@ -218,7 +271,12 @@ impl GenericParams { } } - fn fill_params(&mut self, lower_ctx: &LowerCtx<'_>, params: ast::GenericParamList) { + fn fill_params( + &mut self, + lower_ctx: &LowerCtx<'_>, + params: ast::GenericParamList, + mut add_param_attrs: impl FnMut(AttrOwner, ast::GenericParam), + ) { for type_or_const_param in params.type_or_const_params() { match type_or_const_param { ast::TypeOrConstParam::Type(type_param) => { @@ -232,13 +290,14 @@ impl GenericParams { default, provenance: TypeParamProvenance::TypeParamList, }; - self.type_or_consts.alloc(param.into()); + let idx = self.type_or_consts.alloc(param.into()); let type_ref = TypeRef::Path(name.into()); self.fill_bounds( lower_ctx, type_param.type_bound_list(), Either::Left(type_ref), ); + add_param_attrs(idx.into(), ast::GenericParam::TypeParam(type_param)); } ast::TypeOrConstParam::Const(const_param) => { let name = const_param.name().map_or_else(Name::missing, |it| it.as_name()); @@ -248,9 +307,10 @@ impl GenericParams { let param = ConstParamData { name, ty: Interned::new(ty), - has_default: const_param.default_val().is_some(), + default: ConstRef::from_const_param(lower_ctx, &const_param), }; - self.type_or_consts.alloc(param.into()); + let idx = self.type_or_consts.alloc(param.into()); + add_param_attrs(idx.into(), ast::GenericParam::ConstParam(const_param)); } } } @@ -258,13 +318,14 @@ impl GenericParams { let name = lifetime_param.lifetime().map_or_else(Name::missing, |lt| Name::new_lifetime(<)); let param = LifetimeParamData { name: name.clone() }; - self.lifetimes.alloc(param); + let idx = self.lifetimes.alloc(param); let lifetime_ref = LifetimeRef::new_name(name); self.fill_bounds( lower_ctx, lifetime_param.type_bound_list(), Either::Right(lifetime_ref), ); + add_param_attrs(idx.into(), ast::GenericParam::LifetimeParam(lifetime_param)); } } diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs index 57f023ef35d..75adf21abdc 100644 --- a/crates/hir-def/src/hir/type_ref.rs +++ b/crates/hir-def/src/hir/type_ref.rs @@ -393,6 +393,17 @@ impl ConstRef { Self::Scalar(LiteralConstRef::Unknown) } + pub(crate) fn from_const_param( + lower_ctx: &LowerCtx<'_>, + param: &ast::ConstParam, + ) -> Option { + let default = param.default_val(); + match default { + Some(_) => Some(Self::from_const_arg(lower_ctx, default)), + None => None, + } + } + pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a { struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef); impl fmt::Display for Display<'_> { diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 4b2e5041a12..6038caaf8fc 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -11,6 +11,7 @@ use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; use triomphe::Arc; +use crate::item_scope::ImportOrExternCrate; use crate::{ db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId, ModuleDefId, ModuleId, TraitId, @@ -29,6 +30,8 @@ pub struct ImportInfo { pub container: ModuleId, /// Whether the import is a trait associated item or not. pub is_trait_assoc_item: bool, + /// Whether this item is annotated with `#[doc(hidden)]`. + pub is_doc_hidden: bool, } /// A map from publicly exported items to its name. @@ -109,23 +112,41 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap Some(id.into()), + ImportOrExternCrate::Import(id) => Some(id.import.into()), + } + } else { + match item { + ItemInNs::Types(id) | ItemInNs::Values(id) => id.try_into().ok(), + ItemInNs::Macros(id) => Some(id.into()), + } + }; + let is_doc_hidden = + attr_id.map_or(false, |attr_id| db.attrs(attr_id).has_doc_hidden()); + let import_info = ImportInfo { name: name.clone(), container: module, is_trait_assoc_item: false, + is_doc_hidden, }; match depth_map.entry(item) { - Entry::Vacant(entry) => { - entry.insert(depth); - } + Entry::Vacant(entry) => _ = entry.insert((depth, is_doc_hidden)), Entry::Occupied(mut entry) => { - if depth < *entry.get() { - entry.insert(depth); - } else { + let &(occ_depth, occ_is_doc_hidden) = entry.get(); + // Prefer the one that is not doc(hidden), + // Otherwise, if both have the same doc(hidden)-ness and the new path is shorter, prefer that one. + let overwrite_entry = occ_is_doc_hidden && !is_doc_hidden + || occ_is_doc_hidden == is_doc_hidden && depth < occ_depth; + if !overwrite_entry { continue; } + entry.insert((depth, is_doc_hidden)); } } @@ -162,10 +183,10 @@ fn collect_trait_assoc_items( trait_import_info: &ImportInfo, ) { let _p = profile::span("collect_trait_assoc_items"); - for (assoc_item_name, item) in &db.trait_data(tr).items { + for &(ref assoc_item_name, item) in &db.trait_data(tr).items { let module_def_id = match item { - AssocItemId::FunctionId(f) => ModuleDefId::from(*f), - AssocItemId::ConstId(c) => ModuleDefId::from(*c), + AssocItemId::FunctionId(f) => ModuleDefId::from(f), + AssocItemId::ConstId(c) => ModuleDefId::from(c), // cannot use associated type aliases directly: need a `::TypeAlias` // qualifier, ergo no need to store it for imports in import_map AssocItemId::TypeAliasId(_) => { @@ -183,6 +204,7 @@ fn collect_trait_assoc_items( container: trait_import_info.container, name: assoc_item_name.clone(), is_trait_assoc_item: true, + is_doc_hidden: db.attrs(item.into()).has_doc_hidden(), }; map.insert(assoc_item, assoc_item_info); } diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index 873accafb43..7c11fb9d136 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -6,6 +6,7 @@ use std::collections::hash_map::Entry; use base_db::CrateId; use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId}; use itertools::Itertools; +use la_arena::Idx; use once_cell::sync::Lazy; use profile::Count; use rustc_hash::{FxHashMap, FxHashSet}; @@ -15,16 +16,10 @@ use syntax::ast; use crate::{ db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, - ExternCrateId, HasModule, ImplId, LocalModuleId, MacroId, ModuleDefId, ModuleId, TraitId, - UseId, + ExternCrateId, HasModule, ImplId, LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId, + TraitId, UseId, }; -#[derive(Copy, Clone, Debug)] -pub(crate) enum ImportType { - Glob, - Named, -} - #[derive(Debug, Default)] pub struct PerNsGlobImports { types: FxHashSet<(LocalModuleId, Name)>, @@ -32,15 +27,50 @@ pub struct PerNsGlobImports { macros: FxHashSet<(LocalModuleId, Name)>, } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ImportOrExternCrate { + Import(ImportId), + ExternCrate(ExternCrateId), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) enum ImportType { + Import(ImportId), + Glob(UseId), + ExternCrate(ExternCrateId), +} + +impl ImportOrExternCrate { + pub fn into_import(self) -> Option { + match self { + ImportOrExternCrate::Import(it) => Some(it), + _ => None, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ImportOrDef { + Import(ImportId), + ExternCrate(ExternCrateId), + Def(ModuleDefId), +} +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct ImportId { + pub import: UseId, + pub idx: Idx, +} + #[derive(Debug, Default, PartialEq, Eq)] pub struct ItemScope { _c: Count, /// Defs visible in this scope. This includes `declarations`, but also - /// imports. - types: FxHashMap, - values: FxHashMap, - macros: FxHashMap, + /// imports. The imports belong to this module and can be resolved by using them on + /// the `use_imports_*` fields. + types: FxHashMap)>, + values: FxHashMap)>, + macros: FxHashMap)>, unresolved: FxHashSet, /// The defs declared in this scope. Each def has a single scope where it is @@ -50,7 +80,14 @@ pub struct ItemScope { impls: Vec, unnamed_consts: Vec, /// Traits imported via `use Trait as _;`. - unnamed_trait_imports: FxHashMap, + unnamed_trait_imports: FxHashMap)>, + + // the resolutions of the imports of this scope + use_imports_types: FxHashMap, + use_imports_values: FxHashMap, + use_imports_macros: FxHashMap, + + use_decls: Vec, extern_crate_decls: Vec, /// Macros visible in current module in legacy textual scope /// @@ -82,7 +119,7 @@ struct DeriveMacroInvocation { pub(crate) static BUILTIN_SCOPE: Lazy> = Lazy::new(|| { BuiltinType::ALL .iter() - .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public))) + .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public, None))) .collect() }); @@ -105,11 +142,77 @@ impl ItemScope { .chain(self.values.keys()) .chain(self.macros.keys()) .chain(self.unresolved.iter()) - .sorted() .unique() + .sorted() .map(move |name| (name, self.get(name))) } + pub fn imports(&self) -> impl Iterator + '_ { + self.use_imports_types + .keys() + .copied() + .filter_map(ImportOrExternCrate::into_import) + .chain(self.use_imports_values.keys().copied()) + .chain(self.use_imports_macros.keys().copied()) + .unique() + .sorted() + } + + pub fn fully_resolve_import(&self, db: &dyn DefDatabase, mut import: ImportId) -> PerNs { + let mut res = PerNs::none(); + + let mut def_map; + let mut scope = self; + while let Some(&m) = scope.use_imports_macros.get(&import) { + match m { + ImportOrDef::Import(i) => { + let module_id = i.import.lookup(db).container; + def_map = module_id.def_map(db); + scope = &def_map[module_id.local_id].scope; + import = i; + } + ImportOrDef::Def(ModuleDefId::MacroId(def)) => { + res.macros = Some((def, Visibility::Public, None)); + break; + } + _ => break, + } + } + let mut scope = self; + while let Some(&m) = scope.use_imports_types.get(&ImportOrExternCrate::Import(import)) { + match m { + ImportOrDef::Import(i) => { + let module_id = i.import.lookup(db).container; + def_map = module_id.def_map(db); + scope = &def_map[module_id.local_id].scope; + import = i; + } + ImportOrDef::Def(def) => { + res.types = Some((def, Visibility::Public, None)); + break; + } + _ => break, + } + } + let mut scope = self; + while let Some(&m) = scope.use_imports_values.get(&import) { + match m { + ImportOrDef::Import(i) => { + let module_id = i.import.lookup(db).container; + def_map = module_id.def_map(db); + scope = &def_map[module_id.local_id].scope; + import = i; + } + ImportOrDef::Def(def) => { + res.values = Some((def, Visibility::Public, None)); + break; + } + _ => break, + } + } + res + } + pub fn declarations(&self) -> impl Iterator + '_ { self.declarations.iter().copied() } @@ -121,8 +224,7 @@ impl ItemScope { } pub fn use_decls(&self) -> impl Iterator + ExactSizeIterator + '_ { - // FIXME: to be implemented - std::iter::empty() + self.use_decls.iter().copied() } pub fn impls(&self) -> impl Iterator + ExactSizeIterator + '_ { @@ -132,13 +234,13 @@ impl ItemScope { pub fn values( &self, ) -> impl Iterator + ExactSizeIterator + '_ { - self.values.values().copied() + self.values.values().copied().map(|(a, b, _)| (a, b)) } - pub fn types( + pub(crate) fn types( &self, ) -> impl Iterator + ExactSizeIterator + '_ { - self.types.values().copied() + self.types.values().copied().map(|(def, vis, _)| (def, vis)) } pub fn unnamed_consts(&self) -> impl Iterator + '_ { @@ -165,33 +267,55 @@ impl ItemScope { } pub(crate) fn type_(&self, name: &Name) -> Option<(ModuleDefId, Visibility)> { - self.types.get(name).copied() + self.types.get(name).copied().map(|(a, b, _)| (a, b)) } /// XXX: this is O(N) rather than O(1), try to not introduce new usages. pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { - let (def, mut iter) = match item { - ItemInNs::Macros(def) => { - return self.macros.iter().find_map(|(name, &(other_def, vis))| { - (other_def == def).then_some((name, vis)) - }); - } - ItemInNs::Types(def) => (def, self.types.iter()), - ItemInNs::Values(def) => (def, self.values.iter()), - }; - iter.find_map(|(name, &(other_def, vis))| (other_def == def).then_some((name, vis))) + match item { + ItemInNs::Macros(def) => self + .macros + .iter() + .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))), + ItemInNs::Types(def) => self + .types + .iter() + .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))), + + ItemInNs::Values(def) => self + .values + .iter() + .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))), + } } pub(crate) fn traits(&self) -> impl Iterator + '_ { self.types .values() - .filter_map(|&(def, _)| match def { + .filter_map(|&(def, _, _)| match def { ModuleDefId::TraitId(t) => Some(t), _ => None, }) .chain(self.unnamed_trait_imports.keys().copied()) } + pub(crate) fn resolutions(&self) -> impl Iterator, PerNs)> + '_ { + self.entries().map(|(name, res)| (Some(name.clone()), res)).chain( + self.unnamed_trait_imports.iter().map(|(tr, (vis, i))| { + ( + None, + PerNs::types( + ModuleDefId::TraitId(*tr), + *vis, + i.map(ImportOrExternCrate::Import), + ), + ) + }), + ) + } +} + +impl ItemScope { pub(crate) fn declare(&mut self, def: ModuleDefId) { self.declarations.push(def) } @@ -277,12 +401,14 @@ impl ItemScope { }) } + // FIXME: This is only used in collection, we should move the relevant parts of it out of ItemScope pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option { - self.unnamed_trait_imports.get(&tr).copied() + self.unnamed_trait_imports.get(&tr).copied().map(|(a, _)| a) } pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) { - self.unnamed_trait_imports.insert(tr, vis); + // FIXME: import + self.unnamed_trait_imports.insert(tr, (vis, None)); } pub(crate) fn push_res_with_import( @@ -290,51 +416,187 @@ impl ItemScope { glob_imports: &mut PerNsGlobImports, lookup: (LocalModuleId, Name), def: PerNs, - def_import_type: ImportType, + import: Option, ) -> bool { let mut changed = false; - macro_rules! check_changed { - ( - $changed:ident, - ( $this:ident / $def:ident ) . $field:ident, - $glob_imports:ident [ $lookup:ident ], - $def_import_type:ident - ) => {{ - if let Some(fld) = $def.$field { - let existing = $this.$field.entry($lookup.1.clone()); - match existing { - Entry::Vacant(entry) => { - match $def_import_type { - ImportType::Glob => { - $glob_imports.$field.insert($lookup.clone()); - } - ImportType::Named => { - $glob_imports.$field.remove(&$lookup); - } - } + // FIXME: Document and simplify this - entry.insert(fld); - $changed = true; + if let Some(mut fld) = def.types { + let existing = self.types.entry(lookup.1.clone()); + match existing { + Entry::Vacant(entry) => { + match import { + Some(ImportType::Glob(_)) => { + glob_imports.types.insert(lookup.clone()); } - Entry::Occupied(mut entry) - if matches!($def_import_type, ImportType::Named) => - { - if $glob_imports.$field.remove(&$lookup) { - cov_mark::hit!(import_shadowed); - entry.insert(fld); - $changed = true; + _ => _ = glob_imports.types.remove(&lookup), + } + let import = match import { + Some(ImportType::ExternCrate(extern_crate)) => { + Some(ImportOrExternCrate::ExternCrate(extern_crate)) + } + Some(ImportType::Import(import)) => { + Some(ImportOrExternCrate::Import(import)) + } + None | Some(ImportType::Glob(_)) => None, + }; + let prev = std::mem::replace(&mut fld.2, import); + if let Some(import) = import { + self.use_imports_types.insert( + import, + match prev { + Some(ImportOrExternCrate::Import(import)) => { + ImportOrDef::Import(import) + } + Some(ImportOrExternCrate::ExternCrate(import)) => { + ImportOrDef::ExternCrate(import) + } + None => ImportOrDef::Def(fld.0), + }, + ); + } + entry.insert(fld); + changed = true; + } + Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + if glob_imports.types.remove(&lookup) { + let import = match import { + Some(ImportType::ExternCrate(extern_crate)) => { + Some(ImportOrExternCrate::ExternCrate(extern_crate)) } + Some(ImportType::Import(import)) => { + Some(ImportOrExternCrate::Import(import)) + } + None | Some(ImportType::Glob(_)) => None, + }; + let prev = std::mem::replace(&mut fld.2, import); + if let Some(import) = import { + self.use_imports_types.insert( + import, + match prev { + Some(ImportOrExternCrate::Import(import)) => { + ImportOrDef::Import(import) + } + Some(ImportOrExternCrate::ExternCrate(import)) => { + ImportOrDef::ExternCrate(import) + } + None => ImportOrDef::Def(fld.0), + }, + ); } - _ => {} + cov_mark::hit!(import_shadowed); + entry.insert(fld); + changed = true; } } - }}; + _ => {} + } } - check_changed!(changed, (self / def).types, glob_imports[lookup], def_import_type); - check_changed!(changed, (self / def).values, glob_imports[lookup], def_import_type); - check_changed!(changed, (self / def).macros, glob_imports[lookup], def_import_type); + if let Some(mut fld) = def.values { + let existing = self.values.entry(lookup.1.clone()); + match existing { + Entry::Vacant(entry) => { + match import { + Some(ImportType::Glob(_)) => { + glob_imports.values.insert(lookup.clone()); + } + _ => _ = glob_imports.values.remove(&lookup), + } + let import = match import { + Some(ImportType::Import(import)) => Some(import), + _ => None, + }; + let prev = std::mem::replace(&mut fld.2, import); + if let Some(import) = import { + self.use_imports_values.insert( + import, + match prev { + Some(import) => ImportOrDef::Import(import), + None => ImportOrDef::Def(fld.0), + }, + ); + } + entry.insert(fld); + changed = true; + } + Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + if glob_imports.values.remove(&lookup) { + cov_mark::hit!(import_shadowed); + let import = match import { + Some(ImportType::Import(import)) => Some(import), + _ => None, + }; + let prev = std::mem::replace(&mut fld.2, import); + if let Some(import) = import { + self.use_imports_values.insert( + import, + match prev { + Some(import) => ImportOrDef::Import(import), + None => ImportOrDef::Def(fld.0), + }, + ); + } + entry.insert(fld); + changed = true; + } + } + _ => {} + } + } + + if let Some(mut fld) = def.macros { + let existing = self.macros.entry(lookup.1.clone()); + match existing { + Entry::Vacant(entry) => { + match import { + Some(ImportType::Glob(_)) => { + glob_imports.macros.insert(lookup.clone()); + } + _ => _ = glob_imports.macros.remove(&lookup), + } + let import = match import { + Some(ImportType::Import(import)) => Some(import), + _ => None, + }; + let prev = std::mem::replace(&mut fld.2, import); + if let Some(import) = import { + self.use_imports_macros.insert( + import, + match prev { + Some(import) => ImportOrDef::Import(import), + None => ImportOrDef::Def(fld.0.into()), + }, + ); + } + entry.insert(fld); + changed = true; + } + Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + if glob_imports.macros.remove(&lookup) { + cov_mark::hit!(import_shadowed); + let import = match import { + Some(ImportType::Import(import)) => Some(import), + _ => None, + }; + let prev = std::mem::replace(&mut fld.2, import); + if let Some(import) = import { + self.use_imports_macros.insert( + import, + match prev { + Some(import) => ImportOrDef::Import(import), + None => ImportOrDef::Def(fld.0.into()), + }, + ); + } + entry.insert(fld); + changed = true; + } + } + _ => {} + } + } if def.is_none() && self.unresolved.insert(lookup.1) { changed = true; @@ -343,27 +605,18 @@ impl ItemScope { changed } - pub(crate) fn resolutions(&self) -> impl Iterator, PerNs)> + '_ { - self.entries().map(|(name, res)| (Some(name.clone()), res)).chain( - self.unnamed_trait_imports - .iter() - .map(|(tr, vis)| (None, PerNs::types(ModuleDefId::TraitId(*tr), *vis))), - ) - } - /// Marks everything that is not a procedural macro as private to `this_module`. pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) { self.types .values_mut() - .chain(self.values.values_mut()) + .map(|(def, vis, _)| (def, vis)) + .chain(self.values.values_mut().map(|(def, vis, _)| (def, vis))) .map(|(_, v)| v) - .chain(self.unnamed_trait_imports.values_mut()) + .chain(self.unnamed_trait_imports.values_mut().map(|(vis, _)| vis)) .for_each(|vis| *vis = Visibility::Module(this_module)); - for (mac, vis) in self.macros.values_mut() { - if let MacroId::ProcMacroId(_) = mac { - // FIXME: Technically this is insufficient since reexports of proc macros are also - // forbidden. Practically nobody does that. + for (mac, vis, import) in self.macros.values_mut() { + if matches!(mac, MacroId::ProcMacroId(_) if import.is_none()) { continue; } @@ -382,14 +635,25 @@ impl ItemScope { name.map_or("_".to_string(), |name| name.display(db).to_string()) ); - if def.types.is_some() { + if let Some((.., i)) = def.types { buf.push_str(" t"); + match i { + Some(ImportOrExternCrate::Import(_)) => buf.push('i'), + Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'), + None => (), + } } - if def.values.is_some() { + if let Some((.., i)) = def.values { buf.push_str(" v"); + if i.is_some() { + buf.push('i'); + } } - if def.macros.is_some() { + if let Some((.., i)) = def.macros { buf.push_str(" m"); + if i.is_some() { + buf.push('i'); + } } if def.is_none() { buf.push_str(" _"); @@ -415,10 +679,17 @@ impl ItemScope { attr_macros, derive_macros, extern_crate_decls, + use_decls, + use_imports_values, + use_imports_types, + use_imports_macros, } = self; types.shrink_to_fit(); values.shrink_to_fit(); macros.shrink_to_fit(); + use_imports_types.shrink_to_fit(); + use_imports_values.shrink_to_fit(); + use_imports_macros.shrink_to_fit(); unresolved.shrink_to_fit(); declarations.shrink_to_fit(); impls.shrink_to_fit(); @@ -428,32 +699,44 @@ impl ItemScope { attr_macros.shrink_to_fit(); derive_macros.shrink_to_fit(); extern_crate_decls.shrink_to_fit(); + use_decls.shrink_to_fit(); } } impl PerNs { - pub(crate) fn from_def(def: ModuleDefId, v: Visibility, has_constructor: bool) -> PerNs { + pub(crate) fn from_def( + def: ModuleDefId, + v: Visibility, + has_constructor: bool, + import: Option, + ) -> PerNs { match def { - ModuleDefId::ModuleId(_) => PerNs::types(def, v), - ModuleDefId::FunctionId(_) => PerNs::values(def, v), + ModuleDefId::ModuleId(_) => PerNs::types(def, v, import), + ModuleDefId::FunctionId(_) => { + PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import)) + } ModuleDefId::AdtId(adt) => match adt { - AdtId::UnionId(_) => PerNs::types(def, v), - AdtId::EnumId(_) => PerNs::types(def, v), + AdtId::UnionId(_) => PerNs::types(def, v, import), + AdtId::EnumId(_) => PerNs::types(def, v, import), AdtId::StructId(_) => { if has_constructor { - PerNs::both(def, def, v) + PerNs::both(def, def, v, import) } else { - PerNs::types(def, v) + PerNs::types(def, v, import) } } }, - ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v), - ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v), - ModuleDefId::TraitId(_) => PerNs::types(def, v), - ModuleDefId::TraitAliasId(_) => PerNs::types(def, v), - ModuleDefId::TypeAliasId(_) => PerNs::types(def, v), - ModuleDefId::BuiltinType(_) => PerNs::types(def, v), - ModuleDefId::MacroId(mac) => PerNs::macros(mac, v), + ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v, import), + ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => { + PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import)) + } + ModuleDefId::TraitId(_) => PerNs::types(def, v, import), + ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import), + ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import), + ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import), + ModuleDefId::MacroId(mac) => { + PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::into_import)) + } } } } diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index c9b0f75f1a8..3e1922750b9 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -64,11 +64,11 @@ use triomphe::Arc; use crate::{ attr::Attrs, db::DefDatabase, - generics::GenericParams, + generics::{GenericParams, LifetimeParamData, TypeOrConstParamData}, path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind}, type_ref::{Mutability, TraitRef, TypeBound, TypeRef}, visibility::RawVisibility, - BlockId, + BlockId, Lookup, }; #[derive(Copy, Clone, Eq, PartialEq)] @@ -143,6 +143,16 @@ impl ItemTree { Arc::new(item_tree) } + pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc { + let loc = block.lookup(db); + let block = loc.ast_id.to_node(db.upcast()); + + let ctx = lower::Ctx::new(db, loc.ast_id.file_id); + let mut item_tree = ctx.lower_block(&block); + item_tree.shrink_to_fit(); + Arc::new(item_tree) + } + /// Returns an iterator over all items located at the top level of the `HirFileId` this /// `ItemTree` was created from. pub fn top_level_items(&self) -> &[ModItem] { @@ -178,13 +188,6 @@ impl ItemTree { self.data.get_or_insert_with(Box::default) } - fn block_item_tree(db: &dyn DefDatabase, block: BlockId) -> Arc { - let loc = db.lookup_intern_block(block); - let block = loc.ast_id.to_node(db.upcast()); - let ctx = lower::Ctx::new(db, loc.ast_id.file_id); - Arc::new(ctx.lower_block(&block)) - } - fn shrink_to_fit(&mut self) { if let Some(data) = &mut self.data { let ItemTreeData { @@ -296,10 +299,12 @@ pub enum AttrOwner { Variant(Idx), Field(Idx), Param(Idx), + TypeOrConstParamData(Idx), + LifetimeParamData(Idx), } macro_rules! from_attrs { - ( $( $var:ident($t:ty) ),+ ) => { + ( $( $var:ident($t:ty) ),+ $(,)? ) => { $( impl From<$t> for AttrOwner { fn from(t: $t) -> AttrOwner { @@ -310,7 +315,14 @@ macro_rules! from_attrs { }; } -from_attrs!(ModItem(ModItem), Variant(Idx), Field(Idx), Param(Idx)); +from_attrs!( + ModItem(ModItem), + Variant(Idx), + Field(Idx), + Param(Idx), + TypeOrConstParamData(Idx), + LifetimeParamData(Idx), +); /// Trait implemented by all item nodes in the item tree. pub trait ItemTreeNode: Clone { @@ -373,7 +385,7 @@ impl TreeId { pub(crate) fn item_tree(&self, db: &dyn DefDatabase) -> Arc { match self.block { - Some(block) => ItemTree::block_item_tree(db, block), + Some(block) => db.block_item_tree_query(block), None => db.file_item_tree(self.file), } } @@ -761,6 +773,19 @@ impl Use { lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree"); source_map[index].clone() } + /// Maps a `UseTree` contained in this import back to its AST node. + pub fn use_tree_source_map( + &self, + db: &dyn DefDatabase, + file_id: HirFileId, + ) -> Arena { + // Re-lower the AST item and get the source map. + // Note: The AST unwraps are fine, since if they fail we should have never obtained `index`. + let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast()); + let ast_use_tree = ast.use_tree().expect("missing `use_tree`"); + let hygiene = Hygiene::new(db.upcast(), file_id); + lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree").1 + } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -785,7 +810,7 @@ impl UseTree { fn expand_impl( &self, prefix: Option, - cb: &mut dyn FnMut(Idx, ModPath, ImportKind, Option), + cb: &mut impl FnMut(Idx, ModPath, ImportKind, Option), ) { fn concat_mod_paths( prefix: Option, diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index 7b898e62dba..e4702c113b8 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs @@ -77,6 +77,9 @@ impl<'a> Ctx<'a> { } pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree { + self.tree + .attrs + .insert(AttrOwner::TopLevel, RawAttrs::new(self.db.upcast(), block, self.hygiene())); self.tree.top_level = block .statements() .filter_map(|stmt| match stmt { @@ -602,7 +605,21 @@ impl<'a> Ctx<'a> { generics.fill_bounds(&self.body_ctx, bounds, Either::Left(self_param)); } - generics.fill(&self.body_ctx, node); + let add_param_attrs = |item, param| { + let attrs = RawAttrs::new(self.db.upcast(), ¶m, self.body_ctx.hygiene()); + // This is identical to the body of `Ctx::add_attrs()` but we can't call that here + // because it requires `&mut self` and the call to `generics.fill()` below also + // references `self`. + match self.tree.attrs.entry(item) { + Entry::Occupied(mut entry) => { + *entry.get_mut() = entry.get().merge(attrs); + } + Entry::Vacant(entry) => { + entry.insert(attrs); + } + } + }; + generics.fill(&self.body_ctx, node, add_param_attrs); generics.shrink_to_fit(); Interned::new(generics) @@ -763,7 +780,7 @@ impl UseTreeLowering<'_> { } } -pub(super) fn lower_use_tree( +pub(crate) fn lower_use_tree( db: &dyn DefDatabase, hygiene: &Hygiene, tree: ast::UseTree, diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index da30830fe45..4b852dd613e 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -16,7 +16,7 @@ pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> Strin let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true }; if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) { - p.print_attrs(attrs, true); + p.print_attrs(attrs, true, "\n"); } p.blank(); @@ -84,22 +84,23 @@ impl Printer<'_> { } } - fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool) { + fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool, separated_by: &str) { let inner = if inner { "!" } else { "" }; for attr in &**attrs { - wln!( + w!( self, - "#{}[{}{}]", + "#{}[{}{}]{}", inner, attr.path.display(self.db), attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(), + separated_by, ); } } - fn print_attrs_of(&mut self, of: impl Into) { + fn print_attrs_of(&mut self, of: impl Into, separated_by: &str) { if let Some(attrs) = self.tree.attrs.get(&of.into()) { - self.print_attrs(attrs, false); + self.print_attrs(attrs, false, separated_by); } } @@ -118,7 +119,7 @@ impl Printer<'_> { self.indented(|this| { for field in fields.clone() { let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; - this.print_attrs_of(field); + this.print_attrs_of(field, "\n"); this.print_visibility(*visibility); w!(this, "{}: ", name.display(self.db)); this.print_type_ref(type_ref); @@ -132,7 +133,7 @@ impl Printer<'_> { self.indented(|this| { for field in fields.clone() { let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; - this.print_attrs_of(field); + this.print_attrs_of(field, "\n"); this.print_visibility(*visibility); w!(this, "{}: ", name.display(self.db)); this.print_type_ref(type_ref); @@ -195,7 +196,7 @@ impl Printer<'_> { } fn print_mod_item(&mut self, item: ModItem) { - self.print_attrs_of(item); + self.print_attrs_of(item, "\n"); match item { ModItem::Use(it) => { @@ -261,7 +262,7 @@ impl Printer<'_> { if !params.is_empty() { self.indented(|this| { for param in params.clone() { - this.print_attrs_of(param); + this.print_attrs_of(param, "\n"); match &this.tree[param] { Param::Normal(ty) => { if flags.contains(FnFlags::HAS_SELF_PARAM) { @@ -319,7 +320,7 @@ impl Printer<'_> { self.indented(|this| { for variant in variants.clone() { let Variant { name, fields, ast_id: _ } = &this.tree[variant]; - this.print_attrs_of(variant); + this.print_attrs_of(variant, "\n"); w!(this, "{}", name.display(self.db)); this.print_fields(fields); wln!(this, ","); @@ -484,11 +485,12 @@ impl Printer<'_> { w!(self, "<"); let mut first = true; - for (_, lt) in params.lifetimes.iter() { + for (idx, lt) in params.lifetimes.iter() { if !first { w!(self, ", "); } first = false; + self.print_attrs_of(idx, " "); w!(self, "{}", lt.name.display(self.db)); } for (idx, x) in params.type_or_consts.iter() { @@ -496,6 +498,7 @@ impl Printer<'_> { w!(self, ", "); } first = false; + self.print_attrs_of(idx, " "); match x { TypeOrConstParamData::TypeParamData(ty) => match &ty.name { Some(name) => w!(self, "{}", name.display(self.db)), diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs index 5ded4b6b273..4180f817209 100644 --- a/crates/hir-def/src/item_tree/tests.rs +++ b/crates/hir-def/src/item_tree/tests.rs @@ -358,3 +358,15 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {} "#]], ) } + +#[test] +fn generics_with_attributes() { + check( + r#" +struct S<#[cfg(never)] T>; + "#, + expect![[r#" + pub(self) struct S<#[cfg(never)] T>; + "#]], + ) +} diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 1901db8a0f9..3f87fe62b83 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -109,6 +109,17 @@ impl CrateRootModuleId { } } +impl PartialEq for CrateRootModuleId { + fn eq(&self, other: &ModuleId) -> bool { + other.block.is_none() && other.local_id == DefMap::ROOT && self.krate == other.krate + } +} +impl PartialEq for ModuleId { + fn eq(&self, other: &CrateRootModuleId) -> bool { + other == self + } +} + impl From for ModuleId { fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self { ModuleId { krate, block: None, local_id: DefMap::ROOT } @@ -854,14 +865,36 @@ impl_from!( ConstId, FunctionId, TraitId, + TraitAliasId, TypeAliasId, MacroId(Macro2Id, MacroRulesId, ProcMacroId), ImplId, GenericParamId, - ExternCrateId + ExternCrateId, + UseId for AttrDefId ); +impl TryFrom for AttrDefId { + type Error = (); + + fn try_from(value: ModuleDefId) -> Result { + match value { + ModuleDefId::ModuleId(it) => Ok(it.into()), + ModuleDefId::FunctionId(it) => Ok(it.into()), + ModuleDefId::AdtId(it) => Ok(it.into()), + ModuleDefId::EnumVariantId(it) => Ok(it.into()), + ModuleDefId::ConstId(it) => Ok(it.into()), + ModuleDefId::StaticId(it) => Ok(it.into()), + ModuleDefId::TraitId(it) => Ok(it.into()), + ModuleDefId::TypeAliasId(it) => Ok(it.into()), + ModuleDefId::TraitAliasId(id) => Ok(id.into()), + ModuleDefId::MacroId(id) => Ok(id.into()), + ModuleDefId::BuiltinType(_) => Err(()), + } + } +} + impl From for AttrDefId { fn from(acid: ItemContainerId) -> Self { match acid { @@ -872,6 +905,15 @@ impl From for AttrDefId { } } } +impl From for AttrDefId { + fn from(assoc: AssocItemId) -> Self { + match assoc { + AssocItemId::FunctionId(it) => AttrDefId::FunctionId(it), + AssocItemId::ConstId(it) => AttrDefId::ConstId(it), + AssocItemId::TypeAliasId(it) => AttrDefId::TypeAliasId(it), + } + } +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum VariantId { diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 1250cbb742c..b232651db96 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -238,7 +238,7 @@ fn main() { /* error: expected expression */; /* error: expected expression, expected COMMA */; /* error: expected expression */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(), ::core::fmt::Display::fmt), ]); - /* error: expected expression, expected expression */; + /* error: expected expression, expected R_PAREN */; ::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(5), ::core::fmt::Display::fmt), ]); } "##]], diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index d8e4a4dcc7c..97554f93f1c 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -909,3 +909,68 @@ macro_rules! with_std { "##]], ) } + +#[test] +fn eager_regression_15403() { + check( + r#" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! format_args {} + +fn main() { + format_args /* +errors */ !("{}", line.1.); +} + +"#, + expect![[r##" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! format_args {} + +fn main() { + /* error: expected field name or number *//* parse error: expected field name or number */ +::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(line.1.), ::core::fmt::Display::fmt), ]); +} + +"##]], + ); +} + +#[test] +fn eager_regression_154032() { + check( + r#" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! format_args {} + +fn main() { + format_args /* +errors */ !("{}", &[0 2]); +} + +"#, + expect![[r##" +#[rustc_builtin_macro] +#[macro_export] +macro_rules! format_args {} + +fn main() { + /* error: expected COMMA, expected R_BRACK, expected COMMA, expected COMMA, expected expression, expected R_PAREN *//* parse error: expected COMMA */ +/* parse error: expected R_BRACK */ +/* parse error: expected COMMA */ +/* parse error: expected COMMA */ +/* parse error: expected expression */ +/* parse error: expected R_PAREN */ +/* parse error: expected R_PAREN */ +/* parse error: expected expression, item or let statement */ +/* parse error: expected expression, item or let statement */ +/* parse error: expected expression, item or let statement */ +/* parse error: expected expression, item or let statement */ +/* parse error: expected expression, item or let statement */ +::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(&[0 2]), ::core::fmt::Display::fmt), ]); +} + +"##]], + ); +} diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs index 7a87e61c693..8adced4e082 100644 --- a/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -131,7 +131,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream .as_call_id_with_errors(&db, krate, |path| { resolver .resolve_path_as_macro(&db, &path, Some(MacroSubNs::Bang)) - .map(|it| macro_id_to_def_id(&db, it)) + .map(|(it, _)| macro_id_to_def_id(&db, it)) }) .unwrap(); let macro_call_id = res.value.unwrap(); diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 86818ce26dd..f2110410980 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -60,7 +60,7 @@ mod tests; use std::{cmp::Ord, ops::Deref}; use base_db::{CrateId, Edition, FileId, ProcMacroKind}; -use hir_expand::{name::Name, HirFileId, InFile, MacroCallId, MacroDefId}; +use hir_expand::{ast_id_map::FileAstId, name::Name, HirFileId, InFile, MacroCallId, MacroDefId}; use itertools::Itertools; use la_arena::Arena; use profile::Count; @@ -77,8 +77,8 @@ use crate::{ path::ModPath, per_ns::PerNs, visibility::Visibility, - AstId, BlockId, BlockLoc, CrateRootModuleId, FunctionId, LocalModuleId, Lookup, MacroExpander, - MacroId, ModuleId, ProcMacroId, + AstId, BlockId, BlockLoc, CrateRootModuleId, ExternCrateId, FunctionId, LocalModuleId, Lookup, + MacroExpander, MacroId, ModuleId, ProcMacroId, UseId, }; /// Contains the results of (early) name resolution. @@ -105,10 +105,11 @@ pub struct DefMap { /// The prelude is empty for non-block DefMaps (unless `#[prelude_import]` was used, /// but that attribute is nightly and when used in a block, it affects resolution globally /// so we aren't handling this correctly anyways). - prelude: Option, + prelude: Option<(ModuleId, Option)>, /// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that /// this contains all kinds of macro, not just `macro_rules!` macro. - macro_use_prelude: FxHashMap, + /// ExternCrateId being None implies it being imported from the general prelude import. + macro_use_prelude: FxHashMap)>, /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper /// attributes. @@ -125,7 +126,7 @@ pub struct DefMap { #[derive(Clone, Debug, PartialEq, Eq)] struct DefMapCrateData { /// The extern prelude which contains all root modules of external crates that are in scope. - extern_prelude: FxHashMap, + extern_prelude: FxHashMap)>, /// Side table for resolving derive helpers. exported_derives: FxHashMap>, @@ -217,16 +218,17 @@ pub enum ModuleOrigin { /// Note that non-inline modules, by definition, live inside non-macro file. File { is_mod_rs: bool, - declaration: AstId, + declaration: FileAstId, declaration_tree_id: ItemTreeId, definition: FileId, }, Inline { definition_tree_id: ItemTreeId, - definition: AstId, + definition: FileAstId, }, /// Pseudo-module introduced by a block scope (contains only inner items). BlockExpr { + id: BlockId, block: AstId, }, } @@ -234,8 +236,12 @@ pub enum ModuleOrigin { impl ModuleOrigin { pub fn declaration(&self) -> Option> { match self { - ModuleOrigin::File { declaration: module, .. } - | ModuleOrigin::Inline { definition: module, .. } => Some(*module), + &ModuleOrigin::File { declaration, declaration_tree_id, .. } => { + Some(AstId::new(declaration_tree_id.file_id(), declaration)) + } + &ModuleOrigin::Inline { definition, definition_tree_id } => { + Some(AstId::new(definition_tree_id.file_id(), definition)) + } ModuleOrigin::CrateRoot { .. } | ModuleOrigin::BlockExpr { .. } => None, } } @@ -260,16 +266,17 @@ impl ModuleOrigin { /// That is, a file or a `mod foo {}` with items. fn definition_source(&self, db: &dyn DefDatabase) -> InFile { match self { - ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => { - let file_id = *definition; - let sf = db.parse(file_id).tree(); - InFile::new(file_id.into(), ModuleSource::SourceFile(sf)) + &ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => { + let sf = db.parse(definition).tree(); + InFile::new(definition.into(), ModuleSource::SourceFile(sf)) } - ModuleOrigin::Inline { definition, .. } => InFile::new( - definition.file_id, - ModuleSource::Module(definition.to_node(db.upcast())), + &ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new( + definition_tree_id.file_id(), + ModuleSource::Module( + AstId::new(definition_tree_id.file_id(), definition).to_node(db.upcast()), + ), ), - ModuleOrigin::BlockExpr { block } => { + ModuleOrigin::BlockExpr { block, .. } => { InFile::new(block.file_id, ModuleSource::BlockExpr(block.to_node(db.upcast()))) } } @@ -314,9 +321,7 @@ impl DefMap { } pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc { - let block: BlockLoc = db.lookup_intern_block(block_id); - - let tree_id = TreeId::new(block.ast_id.file_id, Some(block_id)); + let block: BlockLoc = block_id.lookup(db); let parent_map = block.module.def_map(db); let krate = block.module.krate; @@ -325,8 +330,10 @@ impl DefMap { // modules declared by blocks with items. At the moment, we don't use // this visibility for anything outside IDE, so that's probably OK. let visibility = Visibility::Module(ModuleId { krate, local_id, block: None }); - let module_data = - ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility); + let module_data = ModuleData::new( + ModuleOrigin::BlockExpr { block: block.ast_id, id: block_id }, + visibility, + ); let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data); def_map.data = parent_map.data.clone(); @@ -338,7 +345,8 @@ impl DefMap { }, }); - let def_map = collector::collect_defs(db, def_map, tree_id); + let def_map = + collector::collect_defs(db, def_map, TreeId::new(block.ast_id.file_id, Some(block_id))); Arc::new(def_map) } @@ -427,15 +435,19 @@ impl DefMap { self.block.map(|block| block.block) } - pub(crate) fn prelude(&self) -> Option { + pub(crate) fn prelude(&self) -> Option<(ModuleId, Option)> { self.prelude } - pub(crate) fn extern_prelude(&self) -> impl Iterator + '_ { - self.data.extern_prelude.iter().map(|(name, &def)| (name, def.into())) + pub(crate) fn extern_prelude( + &self, + ) -> impl Iterator))> + '_ { + self.data.extern_prelude.iter().map(|(name, &def)| (name, def)) } - pub(crate) fn macro_use_prelude(&self) -> impl Iterator + '_ { + pub(crate) fn macro_use_prelude( + &self, + ) -> impl Iterator))> + '_ { self.macro_use_prelude.iter().map(|(name, &def)| (name, def)) } @@ -638,8 +650,8 @@ impl ModuleData { ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => { definition.into() } - ModuleOrigin::Inline { definition, .. } => definition.file_id, - ModuleOrigin::BlockExpr { block } => block.file_id, + ModuleOrigin::Inline { definition_tree_id, .. } => definition_tree_id.file_id(), + ModuleOrigin::BlockExpr { block, .. } => block.file_id, } } diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index eef54fc492e..e9e71a8747f 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -33,7 +33,7 @@ use crate::{ attr_macro_as_call_id, db::DefDatabase, derive_macro_as_call_id, - item_scope::{ImportType, PerNsGlobImports}, + item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports}, item_tree::{ self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId, @@ -52,10 +52,10 @@ use crate::{ tt, visibility::{RawVisibility, Visibility}, AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantId, - ExternBlockLoc, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, - LocalModuleId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, - ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, - TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseLoc, + ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, + ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, + MacroRulesId, MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, + StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc, }; static GLOB_RECURSION_LIMIT: Limit = Limit::new(100); @@ -146,8 +146,8 @@ impl PartialResolvedImport { #[derive(Clone, Debug, Eq, PartialEq)] enum ImportSource { - Use { id: ItemTreeId, use_tree: Idx }, - ExternCrate(ItemTreeId), + Use { use_tree: Idx, id: UseId, is_prelude: bool, kind: ImportKind }, + ExternCrate { id: ExternCrateId }, } #[derive(Debug, Eq, PartialEq)] @@ -155,54 +155,41 @@ struct Import { path: ModPath, alias: Option, visibility: RawVisibility, - kind: ImportKind, source: ImportSource, - is_prelude: bool, - is_macro_use: bool, } impl Import { fn from_use( - db: &dyn DefDatabase, - krate: CrateId, tree: &ItemTree, - id: ItemTreeId, + item_tree_id: ItemTreeId, + id: UseId, + is_prelude: bool, mut cb: impl FnMut(Self), ) { - let it = &tree[id.value]; - let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into()); + let it = &tree[item_tree_id.value]; let visibility = &tree[it.visibility]; - let is_prelude = attrs.by_key("prelude_import").exists(); it.use_tree.expand(|idx, path, kind, alias| { cb(Self { path, alias, visibility: visibility.clone(), - kind, - is_prelude, - is_macro_use: false, - source: ImportSource::Use { id, use_tree: idx }, + source: ImportSource::Use { use_tree: idx, id, is_prelude, kind }, }); }); } fn from_extern_crate( - db: &dyn DefDatabase, - krate: CrateId, tree: &ItemTree, - id: ItemTreeId, + item_tree_id: ItemTreeId, + id: ExternCrateId, ) -> Self { - let it = &tree[id.value]; - let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into()); + let it = &tree[item_tree_id.value]; let visibility = &tree[it.visibility]; Self { path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())), alias: it.alias.clone(), visibility: visibility.clone(), - kind: ImportKind::Plain, - is_prelude: false, - is_macro_use: attrs.by_key("macro_use").exists(), - source: ImportSource::ExternCrate(id), + source: ImportSource::ExternCrate { id }, } } } @@ -235,7 +222,7 @@ struct DefCollector<'a> { db: &'a dyn DefDatabase, def_map: DefMap, deps: FxHashMap, - glob_imports: FxHashMap>, + glob_imports: FxHashMap>, unresolved_imports: Vec, indeterminate_imports: Vec, unresolved_macros: Vec, @@ -280,7 +267,7 @@ impl DefCollector<'_> { if dep.is_prelude() { crate_data .extern_prelude - .insert(name.clone(), CrateRootModuleId { krate: dep.crate_id }); + .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None)); } } @@ -556,8 +543,12 @@ impl DefCollector<'_> { self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None); match per_ns.types { - Some((ModuleDefId::ModuleId(m), _)) => { - self.def_map.prelude = Some(m); + Some((ModuleDefId::ModuleId(m), _, import)) => { + // FIXME: This should specifically look for a glob import somehow and record that here + self.def_map.prelude = Some(( + m, + import.and_then(ImportOrExternCrate::into_import).map(|it| it.import), + )); } types => { tracing::debug!( @@ -657,9 +648,9 @@ impl DefCollector<'_> { self.def_map.modules[module_id].scope.declare(macro_.into()); self.update( module_id, - &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))], + &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))], Visibility::Public, - ImportType::Named, + None, ); } } @@ -693,9 +684,9 @@ impl DefCollector<'_> { self.def_map.modules[module_id].scope.declare(macro_.into()); self.update( module_id, - &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))], + &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))], vis, - ImportType::Named, + None, ); } @@ -708,9 +699,9 @@ impl DefCollector<'_> { self.def_map.modules[module_id].scope.declare(macro_.into()); self.update( module_id, - &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))], + &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))], Visibility::Public, - ImportType::Named, + None, ); } @@ -720,21 +711,29 @@ impl DefCollector<'_> { /// Exported macros are just all macros in the root module scope. /// Note that it contains not only all `#[macro_export]` macros, but also all aliases /// created by `use` in the root module, ignoring the visibility of `use`. - fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option>) { + fn import_macros_from_extern_crate( + &mut self, + krate: CrateId, + names: Option>, + extern_crate: Option, + ) { let def_map = self.db.crate_def_map(krate); // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!` // macros. let root_scope = &def_map[DefMap::ROOT].scope; - if let Some(names) = names { - for name in names { - // FIXME: Report diagnostic on 404. - if let Some(def) = root_scope.get(&name).take_macros() { - self.def_map.macro_use_prelude.insert(name, def); + match names { + Some(names) => { + for name in names { + // FIXME: Report diagnostic on 404. + if let Some(def) = root_scope.get(&name).take_macros() { + self.def_map.macro_use_prelude.insert(name, (def, extern_crate)); + } } } - } else { - for (name, def) in root_scope.macros() { - self.def_map.macro_use_prelude.insert(name.clone(), def); + None => { + for (name, def) in root_scope.macros() { + self.def_map.macro_use_prelude.insert(name.clone(), (def, extern_crate)); + } } } } @@ -771,48 +770,53 @@ impl DefCollector<'_> { let _p = profile::span("resolve_import") .detail(|| format!("{}", import.path.display(self.db.upcast()))); tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition); - if matches!(import.source, ImportSource::ExternCrate { .. }) { - let name = import - .path - .as_ident() - .expect("extern crate should have been desugared to one-element path"); + match import.source { + ImportSource::ExternCrate { .. } => { + let name = import + .path + .as_ident() + .expect("extern crate should have been desugared to one-element path"); - let res = self.resolve_extern_crate(name); + let res = self.resolve_extern_crate(name); - match res { - Some(res) => { - PartialResolvedImport::Resolved(PerNs::types(res.into(), Visibility::Public)) - } - None => PartialResolvedImport::Unresolved, - } - } else { - let res = self.def_map.resolve_path_fp_with_macro( - self.db, - ResolveMode::Import, - module_id, - &import.path, - BuiltinShadowMode::Module, - None, // An import may resolve to any kind of macro. - ); - - let def = res.resolved_def; - if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() { - return PartialResolvedImport::Unresolved; - } - - if let Some(krate) = res.krate { - if krate != self.def_map.krate { - return PartialResolvedImport::Resolved( - def.filter_visibility(|v| matches!(v, Visibility::Public)), - ); + match res { + Some(res) => PartialResolvedImport::Resolved(PerNs::types( + res.into(), + Visibility::Public, + None, + )), + None => PartialResolvedImport::Unresolved, } } + ImportSource::Use { .. } => { + let res = self.def_map.resolve_path_fp_with_macro( + self.db, + ResolveMode::Import, + module_id, + &import.path, + BuiltinShadowMode::Module, + None, // An import may resolve to any kind of macro. + ); - // Check whether all namespaces are resolved. - if def.is_full() { - PartialResolvedImport::Resolved(def) - } else { - PartialResolvedImport::Indeterminate(def) + let def = res.resolved_def; + if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() { + return PartialResolvedImport::Unresolved; + } + + if let Some(krate) = res.krate { + if krate != self.def_map.krate { + return PartialResolvedImport::Resolved( + def.filter_visibility(|v| matches!(v, Visibility::Public)), + ); + } + } + + // Check whether all namespaces are resolved. + if def.is_full() { + PartialResolvedImport::Resolved(def) + } else { + PartialResolvedImport::Indeterminate(def) + } } } } @@ -837,8 +841,9 @@ impl DefCollector<'_> { .resolve_visibility(self.db, module_id, &directive.import.visibility, false) .unwrap_or(Visibility::Public); - match import.kind { - ImportKind::Plain | ImportKind::TypeOnly => { + match import.source { + ImportSource::ExternCrate { .. } + | ImportSource::Use { kind: ImportKind::Plain | ImportKind::TypeOnly, .. } => { let name = match &import.alias { Some(ImportAlias::Alias(name)) => Some(name), Some(ImportAlias::Underscore) => None, @@ -851,40 +856,44 @@ impl DefCollector<'_> { }, }; - if import.kind == ImportKind::TypeOnly { - def.values = None; - def.macros = None; - } - + let imp = match import.source { + // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 + ImportSource::ExternCrate { id, .. } => { + if self.def_map.block.is_none() && module_id == DefMap::ROOT { + if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = + (def.take_types(), name) + { + if let Ok(def) = def.try_into() { + Arc::get_mut(&mut self.def_map.data) + .unwrap() + .extern_prelude + .insert(name.clone(), (def, Some(id))); + } + } + } + ImportType::ExternCrate(id) + } + ImportSource::Use { kind, id, use_tree, .. } => { + if kind == ImportKind::TypeOnly { + def.values = None; + def.macros = None; + } + ImportType::Import(ImportId { import: id, idx: use_tree }) + } + }; tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); - // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 - if matches!(import.source, ImportSource::ExternCrate { .. }) - && self.def_map.block.is_none() - && module_id == DefMap::ROOT - { - if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name) - { - if let Ok(def) = def.try_into() { - Arc::get_mut(&mut self.def_map.data) - .unwrap() - .extern_prelude - .insert(name.clone(), def); - } - } - } - - self.update(module_id, &[(name.cloned(), def)], vis, ImportType::Named); + self.update(module_id, &[(name.cloned(), def)], vis, Some(imp)); } - ImportKind::Glob => { + ImportSource::Use { kind: ImportKind::Glob, id, .. } => { tracing::debug!("glob import: {:?}", import); match def.take_types() { Some(ModuleDefId::ModuleId(m)) => { - if import.is_prelude { + if let ImportSource::Use { id, is_prelude: true, .. } = import.source { // Note: This dodgily overrides the injected prelude. The rustc // implementation seems to work the same though. cov_mark::hit!(std_prelude); - self.def_map.prelude = Some(m); + self.def_map.prelude = Some((m, Some(id))); } else if m.krate != self.def_map.krate { cov_mark::hit!(glob_across_crates); // glob import from other crate => we can just import everything once @@ -901,7 +910,7 @@ impl DefCollector<'_> { .filter(|(_, res)| !res.is_none()) .collect::>(); - self.update(module_id, &items, vis, ImportType::Glob); + self.update(module_id, &items, vis, Some(ImportType::Glob(id))); } else { // glob import from same crate => we do an initial // import, and then need to propagate any further @@ -933,11 +942,11 @@ impl DefCollector<'_> { .filter(|(_, res)| !res.is_none()) .collect::>(); - self.update(module_id, &items, vis, ImportType::Glob); + self.update(module_id, &items, vis, Some(ImportType::Glob(id))); // record the glob import in case we add further items let glob = self.glob_imports.entry(m.local_id).or_default(); - if !glob.iter().any(|(mid, _)| *mid == module_id) { - glob.push((module_id, vis)); + if !glob.iter().any(|(mid, _, _)| *mid == module_id) { + glob.push((module_id, vis, id)); } } } @@ -959,11 +968,11 @@ impl DefCollector<'_> { .map(|(local_id, variant_data)| { let name = variant_data.name.clone(); let variant = EnumVariantId { parent: e, local_id }; - let res = PerNs::both(variant.into(), variant.into(), vis); + let res = PerNs::both(variant.into(), variant.into(), vis, None); (Some(name), res) }) .collect::>(); - self.update(module_id, &resolutions, vis, ImportType::Glob); + self.update(module_id, &resolutions, vis, Some(ImportType::Glob(id))); } Some(d) => { tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d); @@ -983,10 +992,10 @@ impl DefCollector<'_> { resolutions: &[(Option, PerNs)], // Visibility this import will have vis: Visibility, - import_type: ImportType, + import: Option, ) { self.db.unwind_if_cancelled(); - self.update_recursive(module_id, resolutions, vis, import_type, 0) + self.update_recursive(module_id, resolutions, vis, import, 0) } fn update_recursive( @@ -997,7 +1006,7 @@ impl DefCollector<'_> { // All resolutions are imported with this visibility; the visibilities in // the `PerNs` values are ignored and overwritten vis: Visibility, - import_type: ImportType, + import: Option, depth: usize, ) { if GLOB_RECURSION_LIMIT.check(depth).is_err() { @@ -1014,7 +1023,7 @@ impl DefCollector<'_> { &mut self.from_glob_import, (module_id, name.clone()), res.with_visibility(vis), - import_type, + import, ); } None => { @@ -1059,7 +1068,7 @@ impl DefCollector<'_> { .get(&module_id) .into_iter() .flatten() - .filter(|(glob_importing_module, _)| { + .filter(|(glob_importing_module, _, _)| { // we know all resolutions have the same visibility (`vis`), so we // just need to check that once vis.is_visible_from_def_map(self.db, &self.def_map, *glob_importing_module) @@ -1067,12 +1076,12 @@ impl DefCollector<'_> { .cloned() .collect::>(); - for (glob_importing_module, glob_import_vis) in glob_imports { + for (glob_importing_module, glob_import_vis, use_) in glob_imports { self.update_recursive( glob_importing_module, resolutions, glob_import_vis, - ImportType::Glob, + Some(ImportType::Glob(use_)), depth + 1, ); } @@ -1460,31 +1469,34 @@ impl DefCollector<'_> { // heuristic, but it works in practice. let mut diagnosed_extern_crates = FxHashSet::default(); for directive in &self.unresolved_imports { - if let ImportSource::ExternCrate(krate) = directive.import.source { - let item_tree = krate.item_tree(self.db); - let extern_crate = &item_tree[krate.value]; + if let ImportSource::ExternCrate { id } = directive.import.source { + let item_tree_id = id.lookup(self.db).id; + let item_tree = item_tree_id.item_tree(self.db); + let extern_crate = &item_tree[item_tree_id.value]; diagnosed_extern_crates.insert(extern_crate.name.clone()); self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate( directive.module_id, - InFile::new(krate.file_id(), extern_crate.ast_id), + InFile::new(item_tree_id.file_id(), extern_crate.ast_id), )); } } for directive in &self.unresolved_imports { - if let ImportSource::Use { id: import, use_tree } = directive.import.source { + if let ImportSource::Use { use_tree, id, is_prelude: _, kind: _ } = + directive.import.source + { if matches!( (directive.import.path.segments().first(), &directive.import.path.kind), (Some(krate), PathKind::Plain | PathKind::Abs) if diagnosed_extern_crates.contains(krate) ) { continue; } - + let item_tree_id = id.lookup(self.db).id; self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( directive.module_id, - import, + item_tree_id, use_tree, )); } @@ -1519,72 +1531,66 @@ impl ModCollector<'_, '_> { self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone()); // Prelude module is always considered to be `#[macro_use]`. - if let Some(prelude_module) = self.def_collector.def_map.prelude { + if let Some((prelude_module, _use)) = self.def_collector.def_map.prelude { if prelude_module.krate != krate && is_crate_root { cov_mark::hit!(prelude_is_macro_use); - self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None); + self.def_collector.import_macros_from_extern_crate( + prelude_module.krate, + None, + None, + ); } } + let db = self.def_collector.db; + let module_id = self.module_id; + let update_def = + |def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| { + def_collector.def_map.modules[module_id].scope.declare(id); + def_collector.update( + module_id, + &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor, None))], + vis, + None, + ) + }; + let resolve_vis = |def_map: &DefMap, visibility| { + def_map + .resolve_visibility(db, module_id, visibility, false) + .unwrap_or(Visibility::Public) + }; - // This should be processed eagerly instead of deferred to resolving. - // `#[macro_use] extern crate` is hoisted to imports macros before collecting - // any other items. - // - // If we're not at the crate root, `macro_use`d extern crates are an error so let's just - // ignore them. - if is_crate_root { - for &item in items { - if let ModItem::ExternCrate(id) = item { - self.process_macro_use_extern_crate(id); - } - } - } - - for &item in items { - let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into()); + let mut process_mod_item = |item: ModItem| { + let attrs = self.item_tree.attrs(db, krate, item.into()); if let Some(cfg) = attrs.cfg() { if !self.is_cfg_enabled(&cfg) { self.emit_unconfigured_diagnostic(item, &cfg); - continue; + return; } } if let Err(()) = self.resolve_attributes(&attrs, item, container) { // Do not process the item. It has at least one non-builtin attribute, so the // fixed-point algorithm is required to resolve the rest of them. - continue; + return; } - let db = self.def_collector.db; - let module = self.def_collector.def_map.module_id(self.module_id); + let module = self.def_collector.def_map.module_id(module_id); let def_map = &mut self.def_collector.def_map; - let update_def = - |def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| { - def_collector.def_map.modules[self.module_id].scope.declare(id); - def_collector.update( - self.module_id, - &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))], - vis, - ImportType::Named, - ) - }; - let resolve_vis = |def_map: &DefMap, visibility| { - def_map - .resolve_visibility(db, self.module_id, visibility, false) - .unwrap_or(Visibility::Public) - }; match item { ModItem::Mod(m) => self.collect_module(m, &attrs), - ModItem::Use(import_id) => { - let _import_id = - UseLoc { container: module, id: ItemTreeId::new(self.tree_id, import_id) } - .intern(db); + ModItem::Use(item_tree_id) => { + let id = UseLoc { + container: module, + id: ItemTreeId::new(self.tree_id, item_tree_id), + } + .intern(db); + let is_prelude = attrs.by_key("prelude_import").exists(); Import::from_use( - db, - krate, self.item_tree, - ItemTreeId::new(self.tree_id, import_id), + ItemTreeId::new(self.tree_id, item_tree_id), + id, + is_prelude, |import| { self.def_collector.unresolved_imports.push(ImportDirective { module_id: self.module_id, @@ -1594,22 +1600,29 @@ impl ModCollector<'_, '_> { }, ) } - ModItem::ExternCrate(import_id) => { - let extern_crate_id = ExternCrateLoc { + ModItem::ExternCrate(item_tree_id) => { + let id = ExternCrateLoc { container: module, - id: ItemTreeId::new(self.tree_id, import_id), + id: ItemTreeId::new(self.tree_id, item_tree_id), } .intern(db); + if is_crate_root { + self.process_macro_use_extern_crate( + item_tree_id, + id, + attrs.by_key("macro_use").attrs(), + ); + } + self.def_collector.def_map.modules[self.module_id] .scope - .define_extern_crate_decl(extern_crate_id); + .define_extern_crate_decl(id); self.def_collector.unresolved_imports.push(ImportDirective { module_id: self.module_id, import: Import::from_extern_crate( - db, - krate, self.item_tree, - ItemTreeId::new(self.tree_id, import_id), + ItemTreeId::new(self.tree_id, item_tree_id), + id, ), status: PartialResolvedImport::Unresolved, }) @@ -1768,21 +1781,34 @@ impl ModCollector<'_, '_> { ); } } + }; + + // extern crates should be processed eagerly instead of deferred to resolving. + // `#[macro_use] extern crate` is hoisted to imports macros before collecting + // any other items. + if is_crate_root { + items + .iter() + .filter(|it| matches!(it, ModItem::ExternCrate(..))) + .copied() + .for_each(&mut process_mod_item); + items + .iter() + .filter(|it| !matches!(it, ModItem::ExternCrate(..))) + .copied() + .for_each(process_mod_item); + } else { + items.iter().copied().for_each(process_mod_item); } } - fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId) { + fn process_macro_use_extern_crate<'a>( + &mut self, + extern_crate: FileItemTreeId, + extern_crate_id: ExternCrateId, + macro_use_attrs: impl Iterator, + ) { let db = self.def_collector.db; - let attrs = self.item_tree.attrs( - db, - self.def_collector.def_map.krate, - ModItem::from(extern_crate).into(), - ); - if let Some(cfg) = attrs.cfg() { - if !self.is_cfg_enabled(&cfg) { - return; - } - } let target_crate = match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) { @@ -1798,11 +1824,11 @@ impl ModCollector<'_, '_> { let mut single_imports = Vec::new(); let hygiene = Hygiene::new_unhygienic(); - for attr in attrs.by_key("macro_use").attrs() { + for attr in macro_use_attrs { let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else { // `#[macro_use]` (without any paths) found, forget collected names and just import // all visible macros. - self.def_collector.import_macros_from_extern_crate(target_crate, None); + self.def_collector.import_macros_from_extern_crate(target_crate, None, Some(extern_crate_id)); return; }; for path in paths { @@ -1812,7 +1838,11 @@ impl ModCollector<'_, '_> { } } - self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports)); + self.def_collector.import_macros_from_extern_crate( + target_crate, + Some(single_imports), + Some(extern_crate_id), + ); } fn collect_module(&mut self, module_id: FileItemTreeId, attrs: &Attrs) { @@ -1824,7 +1854,7 @@ impl ModCollector<'_, '_> { ModKind::Inline { items } => { let module_id = self.push_child_module( module.name.clone(), - AstId::new(self.file_id(), module.ast_id), + module.ast_id, None, &self.item_tree[module.visibility], module_id, @@ -1862,7 +1892,7 @@ impl ModCollector<'_, '_> { if is_enabled { let module_id = self.push_child_module( module.name.clone(), - ast_id, + ast_id.value, Some((file_id, is_mod_rs)), &self.item_tree[module.visibility], module_id, @@ -1889,7 +1919,7 @@ impl ModCollector<'_, '_> { Err(candidates) => { self.push_child_module( module.name.clone(), - ast_id, + ast_id.value, None, &self.item_tree[module.visibility], module_id, @@ -1906,7 +1936,7 @@ impl ModCollector<'_, '_> { fn push_child_module( &mut self, name: Name, - declaration: AstId, + declaration: FileAstId, definition: Option<(FileId, bool)>, visibility: &crate::visibility::RawVisibility, mod_tree_id: FileItemTreeId, @@ -1948,9 +1978,9 @@ impl ModCollector<'_, '_> { def_map.modules[self.module_id].scope.declare(def); self.def_collector.update( self.module_id, - &[(Some(name), PerNs::from_def(def, vis, false))], + &[(Some(name), PerNs::from_def(def, vis, false, None))], vis, - ImportType::Named, + None, ); res } @@ -2198,7 +2228,7 @@ impl ModCollector<'_, '_> { map[module].scope.get_legacy_macro(name)?.last().copied() }) .or_else(|| def_map[self.module_id].scope.get(name).take_macros()) - .or_else(|| def_map.macro_use_prelude.get(name).copied()) + .or_else(|| Some(def_map.macro_use_prelude.get(name).copied()?.0)) .filter(|&id| { sub_namespace_match( Some(MacroSubNs::from_id(db, id)), diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index de22ea10146..460a908b6db 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -15,8 +15,9 @@ use hir_expand::name::Name; use triomphe::Arc; use crate::{ + data::adt::VariantData, db::DefDatabase, - item_scope::BUILTIN_SCOPE, + item_scope::{ImportOrExternCrate, BUILTIN_SCOPE}, nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs}, path::{ModPath, PathKind}, per_ns::PerNs, @@ -65,7 +66,7 @@ impl PerNs { db: &dyn DefDatabase, expected: Option, ) -> Self { - self.macros = self.macros.filter(|&(id, _)| { + self.macros = self.macros.filter(|&(id, _, _)| { let this = MacroSubNs::from_id(db, id); sub_namespace_match(Some(this), expected) }); @@ -196,15 +197,15 @@ impl DefMap { PathKind::DollarCrate(krate) => { if krate == self.krate { cov_mark::hit!(macro_dollar_crate_self); - PerNs::types(self.crate_root().into(), Visibility::Public) + PerNs::types(self.crate_root().into(), Visibility::Public, None) } else { let def_map = db.crate_def_map(krate); let module = def_map.module_id(Self::ROOT); cov_mark::hit!(macro_dollar_crate_other); - PerNs::types(module.into(), Visibility::Public) + PerNs::types(module.into(), Visibility::Public, None) } } - PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public), + PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public, None), // plain import or absolute path in 2015: crate-relative with // fallback to extern prelude (with the simplification in // rust-lang/rust#57745) @@ -291,7 +292,7 @@ impl DefMap { ); } - PerNs::types(module.into(), Visibility::Public) + PerNs::types(module.into(), Visibility::Public, None) } PathKind::Abs => { // 2018-style absolute path -- only extern prelude @@ -299,9 +300,13 @@ impl DefMap { Some((_, segment)) => segment, None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), }; - if let Some(&def) = self.data.extern_prelude.get(segment) { + if let Some(&(def, extern_crate)) = self.data.extern_prelude.get(segment) { tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def); - PerNs::types(def.into(), Visibility::Public) + PerNs::types( + def.into(), + Visibility::Public, + extern_crate.map(ImportOrExternCrate::ExternCrate), + ) } else { return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude } @@ -309,7 +314,7 @@ impl DefMap { }; for (i, segment) in segments { - let (curr, vis) = match curr_per_ns.take_types_vis() { + let (curr, vis, imp) = match curr_per_ns.take_types_full() { Some(r) => r, None => { // we still have path segments left, but the path so far @@ -364,18 +369,20 @@ impl DefMap { Some(local_id) => { let variant = EnumVariantId { parent: e, local_id }; match &*enum_data.variants[local_id].variant_data { - crate::data::adt::VariantData::Record(_) => { - PerNs::types(variant.into(), Visibility::Public) - } - crate::data::adt::VariantData::Tuple(_) - | crate::data::adt::VariantData::Unit => { - PerNs::both(variant.into(), variant.into(), Visibility::Public) + VariantData::Record(_) => { + PerNs::types(variant.into(), Visibility::Public, None) } + VariantData::Tuple(_) | VariantData::Unit => PerNs::both( + variant.into(), + variant.into(), + Visibility::Public, + None, + ), } } None => { return ResolvePathResult::with( - PerNs::types(e.into(), vis), + PerNs::types(e.into(), vis, imp), ReachedFixedPoint::Yes, Some(i), Some(self.krate), @@ -393,7 +400,7 @@ impl DefMap { ); return ResolvePathResult::with( - PerNs::types(s, vis), + PerNs::types(s, vis, imp), ReachedFixedPoint::Yes, Some(i), Some(self.krate), @@ -430,7 +437,7 @@ impl DefMap { .filter(|&id| { sub_namespace_match(Some(MacroSubNs::from_id(db, id)), expected_macro_subns) }) - .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); + .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public, None)); let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns); let from_builtin = match self.block { Some(_) => { @@ -449,18 +456,27 @@ impl DefMap { let extern_prelude = || { if self.block.is_some() { - // Don't resolve extern prelude in block `DefMap`s. + // Don't resolve extern prelude in block `DefMap`s, defer it to the crate def map so + // that blocks can properly shadow them return PerNs::none(); } - self.data - .extern_prelude - .get(name) - .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public)) + self.data.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| { + PerNs::types( + it.into(), + Visibility::Public, + extern_crate.map(ImportOrExternCrate::ExternCrate), + ) + }) }; let macro_use_prelude = || { - self.macro_use_prelude - .get(name) - .map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public)) + self.macro_use_prelude.get(name).map_or(PerNs::none(), |&(it, _extern_crate)| { + PerNs::macros( + it.into(), + Visibility::Public, + // FIXME? + None, // extern_crate.map(ImportOrExternCrate::ExternCrate), + ) + }) }; let prelude = || self.resolve_in_prelude(db, name); @@ -488,18 +504,23 @@ impl DefMap { // Don't resolve extern prelude in block `DefMap`s. return PerNs::none(); } - self.data - .extern_prelude - .get(name) - .copied() - .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public)) + self.data.extern_prelude.get(name).copied().map_or( + PerNs::none(), + |(it, extern_crate)| { + PerNs::types( + it.into(), + Visibility::Public, + extern_crate.map(ImportOrExternCrate::ExternCrate), + ) + }, + ) }; from_crate_root.or_else(from_extern_prelude) } fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs { - if let Some(prelude) = self.prelude { + if let Some((prelude, _use)) = self.prelude { let keep; let def_map = if prelude.krate == self.krate { self diff --git a/crates/hir-def/src/nameres/tests.rs b/crates/hir-def/src/nameres/tests.rs index dd7c3c36306..e7cc44b04da 100644 --- a/crates/hir-def/src/nameres/tests.rs +++ b/crates/hir-def/src/nameres/tests.rs @@ -168,7 +168,7 @@ pub struct Baz; "#, expect![[r#" crate - Foo: t v + Foo: ti vi foo: t crate::foo @@ -194,8 +194,8 @@ pub enum Quux {}; "#, expect![[r#" crate - Baz: t v - Quux: t + Baz: ti vi + Quux: ti foo: t crate::foo @@ -225,11 +225,11 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v + Baz: ti vi foo: t crate::foo - Baz: t v + Baz: ti vi bar: t crate::foo::bar @@ -274,7 +274,7 @@ use self::E::V; expect![[r#" crate E: t - V: t v + V: ti vi "#]], ); } @@ -307,7 +307,7 @@ pub struct FromLib; crate::foo Bar: _ - FromLib: t v + FromLib: ti vi "#]], ); } @@ -328,7 +328,7 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t + Baz: ti foo: t crate::foo @@ -352,7 +352,7 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v + Baz: ti vi "#]], ); } @@ -375,13 +375,13 @@ pub struct Arc; expect![[r#" crate alloc: t - alloc_crate: t + alloc_crate: te sync: t crate::alloc crate::sync - Arc: t v + Arc: ti vi "#]], ); } @@ -404,13 +404,13 @@ pub struct Arc; expect![[r#" crate alloc: t - alloc_crate: t + alloc_crate: te sync: t crate::alloc crate::sync - Arc: t v + Arc: ti vi "#]], ); } @@ -426,7 +426,7 @@ extern crate self as bla; "#, expect![[r#" crate - bla: t + bla: te "#]], ); } @@ -447,7 +447,7 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v + Baz: ti vi "#]], ); } @@ -465,7 +465,7 @@ pub struct Bar; "#, expect![[r#" crate - Bar: t v + Bar: ti vi foo: v "#]], ); @@ -492,9 +492,9 @@ fn no_std_prelude() { } "#, expect![[r#" - crate - Rust: t v - "#]], + crate + Rust: ti vi + "#]], ); } @@ -516,9 +516,9 @@ fn edition_specific_preludes() { } "#, expect![[r#" - crate - Rust2018: t v - "#]], + crate + Rust2018: ti vi + "#]], ); check( r#" @@ -533,9 +533,9 @@ fn edition_specific_preludes() { } "#, expect![[r#" - crate - Rust2021: t v - "#]], + crate + Rust2021: ti vi + "#]], ); } @@ -563,8 +563,8 @@ pub mod prelude { "#, expect![[r#" crate - Bar: t v - Foo: t v + Bar: ti vi + Foo: ti vi "#]], ); } @@ -590,7 +590,7 @@ pub mod prelude { "#, expect![[r#" crate - Bar: t v + Bar: ti vi Baz: _ Foo: _ "#]], @@ -619,8 +619,8 @@ pub mod prelude { expect![[r#" crate Bar: _ - Baz: t v - Foo: t v + Baz: ti vi + Foo: ti vi "#]], ); } @@ -643,7 +643,7 @@ mod b { "#, expect![[r#" crate - T: t v + T: ti vi a: t b: t @@ -816,8 +816,8 @@ fn bar() {} expect![[r#" crate bar: v - baz: v - foo: t + baz: vi + foo: ti "#]], ); } @@ -836,7 +836,7 @@ use self::m::S::{self}; "#, expect![[r#" crate - S: t + S: ti m: t crate::m @@ -860,8 +860,8 @@ pub const settings: () = (); "#, expect![[r#" crate - Settings: t v - settings: v + Settings: ti vi + settings: vi "#]], ) } @@ -890,8 +890,8 @@ pub struct Struct; "#, expect![[r#" crate - Struct: t v - dep: t + Struct: ti vi + dep: te "#]], ); } @@ -917,13 +917,13 @@ use some_module::unknown_func; crate other_module: t some_module: t - unknown_func: v + unknown_func: vi crate::other_module some_submodule: t crate::other_module::some_submodule - unknown_func: v + unknown_func: vi crate::some_module unknown_func: v diff --git a/crates/hir-def/src/nameres/tests/globs.rs b/crates/hir-def/src/nameres/tests/globs.rs index 88a3c76393f..1ca74b5da6b 100644 --- a/crates/hir-def/src/nameres/tests/globs.rs +++ b/crates/hir-def/src/nameres/tests/globs.rs @@ -24,7 +24,7 @@ pub struct Baz; foo: t crate::foo - Baz: t v + Baz: ti vi Foo: t v bar: t @@ -237,9 +237,9 @@ pub mod baz { pub struct Bar; } "#, expect![[r#" crate - Bar: t v + Bar: ti vi bar: t - baz: t + baz: ti foo: t crate::bar @@ -276,9 +276,9 @@ pub mod baz { pub struct Bar; } "#, expect![[r#" crate - Bar: t v + Bar: ti vi bar: t - baz: t + baz: ti foo: t crate::bar @@ -323,7 +323,7 @@ mod d { X: t v crate::b - foo: t + foo: ti crate::c foo: t @@ -332,8 +332,8 @@ mod d { Y: t v crate::d - Y: t v - foo: t + Y: ti vi + foo: ti "#]], ); } @@ -355,7 +355,7 @@ use event::Event; "#, expect![[r#" crate - Event: t + Event: ti event: t crate::event diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs index 40d3a16540d..4a86f88e57a 100644 --- a/crates/hir-def/src/nameres/tests/incremental.rs +++ b/crates/hir-def/src/nameres/tests/incremental.rs @@ -212,7 +212,7 @@ pub type Ty = (); } for (_, res) in module_data.scope.resolutions() { - match res.values.or(res.types).unwrap().0 { + match res.values.map(|(a, _, _)| a).or(res.types.map(|(a, _, _)| a)).unwrap() { ModuleDefId::FunctionId(f) => _ = db.function_data(f), ModuleDefId::AdtId(adt) => match adt { AdtId::StructId(it) => _ = db.struct_data(it), diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs index f4cca8d68d0..e64fa0b46f1 100644 --- a/crates/hir-def/src/nameres/tests/macros.rs +++ b/crates/hir-def/src/nameres/tests/macros.rs @@ -203,8 +203,8 @@ macro_rules! bar { expect![[r#" crate Foo: t - bar: m - foo: m + bar: mi + foo: mi "#]], ); } @@ -251,7 +251,7 @@ mod priv_mod { Bar: t v Foo: t v bar: t - foo: t + foo: te crate::bar Baz: t v @@ -318,9 +318,9 @@ macro_rules! baz3 { () => { struct OkBaz3; } } OkBaz1: t v OkBaz2: t v OkBaz3: t v - all: t - empty: t - multiple: t + all: te + empty: te + multiple: te "#]], ); } @@ -551,8 +551,8 @@ fn baz() {} "#, expect![[r#" crate - bar: t m - baz: t v m + bar: ti mi + baz: ti v mi foo: t m "#]], ); @@ -583,7 +583,7 @@ mod m { crate Alias: t v Direct: t v - foo: t + foo: te "#]], ); } @@ -628,9 +628,9 @@ mod m { m: t crate::m - alias1: m - alias2: m - alias3: m + alias1: mi + alias2: mi + alias3: mi not_found: _ "#]], ); @@ -682,11 +682,11 @@ pub struct Baz; "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: ti vi + Baz: ti vi Foo: t v - FooSelf: t v - foo: t + FooSelf: ti vi + foo: te m: t crate::m @@ -725,7 +725,7 @@ pub struct bar; "#, expect![[r#" crate - bar: t v + bar: ti vi "#]], ); } @@ -1340,7 +1340,7 @@ pub mod prelude { crate Ok: t v bar: m - dep: t + dep: te foo: m ok: v "#]], @@ -1370,13 +1370,13 @@ macro_rules! mk_foo { } "#, expect![[r#" - crate - a: t - lib: t + crate + a: t + lib: te - crate::a - Ok: t v - "#]], + crate::a + Ok: t v + "#]], ); } @@ -1427,8 +1427,8 @@ pub mod prelude { expect![[r#" crate Ok: t v - bar: m - foo: m + bar: mi + foo: mi ok: v "#]], ); diff --git a/crates/hir-def/src/nameres/tests/mod_resolution.rs b/crates/hir-def/src/nameres/tests/mod_resolution.rs index 81bc0ff91e3..1327d9aa62e 100644 --- a/crates/hir-def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir-def/src/nameres/tests/mod_resolution.rs @@ -80,18 +80,18 @@ pub trait Iterator; prelude: t crate::iter - Iterator: t + Iterator: ti traits: t crate::iter::traits - Iterator: t + Iterator: ti iterator: t crate::iter::traits::iterator Iterator: t crate::prelude - Iterator: t + Iterator: ti "#]], ); } @@ -109,7 +109,7 @@ pub struct Bar; "#, expect![[r#" crate - Bar: t v + Bar: ti vi foo: t crate::foo @@ -139,7 +139,7 @@ pub struct Baz; "#, expect![[r#" crate - Bar: t v + Bar: ti vi r#async: t crate::r#async @@ -176,8 +176,8 @@ pub struct Bar; "#, expect![[r#" crate - Bar: t v - Foo: t v + Bar: ti vi + Foo: ti vi r#async: t crate::r#async @@ -207,7 +207,7 @@ pub struct Bar; "#, expect![[r#" crate - Bar: t v + Bar: ti vi foo: t crate::foo @@ -236,7 +236,7 @@ pub struct Baz; foo: t crate::foo - Baz: t v + Baz: ti vi bar: t crate::foo::bar @@ -265,7 +265,7 @@ pub struct Baz; foo: t crate::foo - Baz: t v + Baz: ti vi bar: t crate::foo::bar @@ -292,7 +292,7 @@ use super::Baz; foo: t crate::foo - Baz: t v + Baz: ti vi "#]], ); } @@ -626,7 +626,7 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v + Baz: ti vi foo: t crate::foo @@ -660,7 +660,7 @@ pub struct Baz; foo: t crate::foo - Baz: t v + Baz: ti vi bar: t crate::foo::bar @@ -694,7 +694,7 @@ pub struct Baz; foo: t crate::foo - Baz: t v + Baz: ti vi bar: t crate::foo::bar @@ -728,7 +728,7 @@ pub struct Baz; foo: t crate::foo - Baz: t v + Baz: ti vi bar: t crate::foo::bar @@ -868,7 +868,7 @@ pub mod hash { pub trait Hash {} } "#, expect![[r#" crate - Hash: t + Hash: ti core: t crate::core diff --git a/crates/hir-def/src/nameres/tests/primitives.rs b/crates/hir-def/src/nameres/tests/primitives.rs index 215e8952d90..271eb1c79b1 100644 --- a/crates/hir-def/src/nameres/tests/primitives.rs +++ b/crates/hir-def/src/nameres/tests/primitives.rs @@ -14,10 +14,10 @@ pub use i32 as int; expect![[r#" crate foo: t - int: t + int: ti crate::foo - int: t + int: ti "#]], ); } diff --git a/crates/hir-def/src/per_ns.rs b/crates/hir-def/src/per_ns.rs index 2bc1f8e926e..14890364d0b 100644 --- a/crates/hir-def/src/per_ns.rs +++ b/crates/hir-def/src/per_ns.rs @@ -3,13 +3,24 @@ //! //! `PerNs` (per namespace) captures this. -use crate::{item_scope::ItemInNs, visibility::Visibility, MacroId, ModuleDefId}; +use crate::{ + item_scope::{ImportId, ImportOrExternCrate, ItemInNs}, + visibility::Visibility, + MacroId, ModuleDefId, +}; + +#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] +pub enum Namespace { + Types, + Values, + Macros, +} #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct PerNs { - pub types: Option<(ModuleDefId, Visibility)>, - pub values: Option<(ModuleDefId, Visibility)>, - pub macros: Option<(MacroId, Visibility)>, + pub types: Option<(ModuleDefId, Visibility, Option)>, + pub values: Option<(ModuleDefId, Visibility, Option)>, + pub macros: Option<(MacroId, Visibility, Option)>, } impl Default for PerNs { @@ -23,20 +34,29 @@ impl PerNs { PerNs { types: None, values: None, macros: None } } - pub fn values(t: ModuleDefId, v: Visibility) -> PerNs { - PerNs { types: None, values: Some((t, v)), macros: None } + pub fn values(t: ModuleDefId, v: Visibility, i: Option) -> PerNs { + PerNs { types: None, values: Some((t, v, i)), macros: None } } - pub fn types(t: ModuleDefId, v: Visibility) -> PerNs { - PerNs { types: Some((t, v)), values: None, macros: None } + pub fn types(t: ModuleDefId, v: Visibility, i: Option) -> PerNs { + PerNs { types: Some((t, v, i)), values: None, macros: None } } - pub fn both(types: ModuleDefId, values: ModuleDefId, v: Visibility) -> PerNs { - PerNs { types: Some((types, v)), values: Some((values, v)), macros: None } + pub fn both( + types: ModuleDefId, + values: ModuleDefId, + v: Visibility, + i: Option, + ) -> PerNs { + PerNs { + types: Some((types, v, i)), + values: Some((values, v, i.and_then(ImportOrExternCrate::into_import))), + macros: None, + } } - pub fn macros(macro_: MacroId, v: Visibility) -> PerNs { - PerNs { types: None, values: None, macros: Some((macro_, v)) } + pub fn macros(macro_: MacroId, v: Visibility, i: Option) -> PerNs { + PerNs { types: None, values: None, macros: Some((macro_, v, i)) } } pub fn is_none(&self) -> bool { @@ -51,7 +71,7 @@ impl PerNs { self.types.map(|it| it.0) } - pub fn take_types_vis(self) -> Option<(ModuleDefId, Visibility)> { + pub fn take_types_full(self) -> Option<(ModuleDefId, Visibility, Option)> { self.types } @@ -59,24 +79,32 @@ impl PerNs { self.values.map(|it| it.0) } + pub fn take_values_import(self) -> Option<(ModuleDefId, Option)> { + self.values.map(|it| (it.0, it.2)) + } + pub fn take_macros(self) -> Option { self.macros.map(|it| it.0) } + pub fn take_macros_import(self) -> Option<(MacroId, Option)> { + self.macros.map(|it| (it.0, it.2)) + } + pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs { let _p = profile::span("PerNs::filter_visibility"); PerNs { - types: self.types.filter(|(_, v)| f(*v)), - values: self.values.filter(|(_, v)| f(*v)), - macros: self.macros.filter(|(_, v)| f(*v)), + types: self.types.filter(|&(_, v, _)| f(v)), + values: self.values.filter(|&(_, v, _)| f(v)), + macros: self.macros.filter(|&(_, v, _)| f(v)), } } pub fn with_visibility(self, vis: Visibility) -> PerNs { PerNs { - types: self.types.map(|(it, _)| (it, vis)), - values: self.values.map(|(it, _)| (it, vis)), - macros: self.macros.map(|(it, _)| (it, vis)), + types: self.types.map(|(it, _, c)| (it, vis, c)), + values: self.values.map(|(it, _, c)| (it, vis, c)), + macros: self.macros.map(|(it, _, import)| (it, vis, import)), } } @@ -96,12 +124,20 @@ impl PerNs { } } - pub fn iter_items(self) -> impl Iterator { + pub fn iter_items(self) -> impl Iterator)> { let _p = profile::span("PerNs::iter_items"); self.types - .map(|it| ItemInNs::Types(it.0)) + .map(|it| (ItemInNs::Types(it.0), it.2)) .into_iter() - .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter()) - .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter()) + .chain( + self.values + .map(|it| (ItemInNs::Values(it.0), it.2.map(ImportOrExternCrate::Import))) + .into_iter(), + ) + .chain( + self.macros + .map(|it| (ItemInNs::Macros(it.0), it.2.map(ImportOrExternCrate::Import))) + .into_iter(), + ) } } diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index b112c1070d4..2f9187009e2 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -12,20 +12,21 @@ use triomphe::Arc; use crate::{ body::scope::{ExprScopes, ScopeId}, builtin_type::BuiltinType, + data::ExternCrateDeclData, db::DefDatabase, generics::{GenericParams, TypeOrConstParamData}, hir::{BindingId, ExprId, LabelId}, - item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, + item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE}, lang_item::LangItemTarget, nameres::{DefMap, MacroSubNs}, path::{ModPath, Path, PathKind}, per_ns::PerNs, visibility::{RawVisibility, Visibility}, - AdtId, AssocItemId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, - EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, - HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, - MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, - TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId, + AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, + ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, + ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId, + ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, + TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId, }; #[derive(Debug, Clone)] @@ -100,8 +101,8 @@ pub enum TypeNs { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ResolveValueResult { - ValueNs(ValueNs), - Partial(TypeNs, usize), + ValueNs(ValueNs, Option), + Partial(TypeNs, usize, Option), } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -148,39 +149,11 @@ impl Resolver { self.resolve_module_path(db, path, BuiltinShadowMode::Module) } - // FIXME: This shouldn't exist - pub fn resolve_module_path_in_trait_assoc_items( - &self, - db: &dyn DefDatabase, - path: &ModPath, - ) -> Option { - let (item_map, module) = self.item_scope(); - let (module_res, idx) = - item_map.resolve_path(db, module, path, BuiltinShadowMode::Module, None); - match module_res.take_types()? { - ModuleDefId::TraitId(it) => { - let idx = idx?; - let unresolved = &path.segments()[idx..]; - let assoc = match unresolved { - [it] => it, - _ => return None, - }; - let &(_, assoc) = db.trait_data(it).items.iter().find(|(n, _)| n == assoc)?; - Some(match assoc { - AssocItemId::FunctionId(it) => PerNs::values(it.into(), Visibility::Public), - AssocItemId::ConstId(it) => PerNs::values(it.into(), Visibility::Public), - AssocItemId::TypeAliasId(it) => PerNs::types(it.into(), Visibility::Public), - }) - } - _ => None, - } - } - pub fn resolve_path_in_type_ns( &self, db: &dyn DefDatabase, path: &Path, - ) -> Option<(TypeNs, Option)> { + ) -> Option<(TypeNs, Option, Option)> { let path = match path { Path::Normal { mod_path, .. } => mod_path, Path::LangItem(l) => { @@ -197,6 +170,7 @@ impl Resolver { | LangItemTarget::Static(_) => return None, }, None, + None, )) } }; @@ -213,17 +187,17 @@ impl Resolver { Scope::ExprScope(_) => continue, Scope::GenericParams { params, def } => { if let Some(id) = params.find_type_by_name(first_name, *def) { - return Some((TypeNs::GenericParam(id), remaining_idx())); + return Some((TypeNs::GenericParam(id), remaining_idx(), None)); } } &Scope::ImplDefScope(impl_) => { if first_name == &name![Self] { - return Some((TypeNs::SelfType(impl_), remaining_idx())); + return Some((TypeNs::SelfType(impl_), remaining_idx(), None)); } } &Scope::AdtScope(adt) => { if first_name == &name![Self] { - return Some((TypeNs::AdtSelfType(adt), remaining_idx())); + return Some((TypeNs::AdtSelfType(adt), remaining_idx(), None)); } } Scope::BlockScope(m) => { @@ -236,12 +210,24 @@ impl Resolver { self.module_scope.resolve_path_in_type_ns(db, path) } + pub fn resolve_path_in_type_ns_fully_with_imports( + &self, + db: &dyn DefDatabase, + path: &Path, + ) -> Option<(TypeNs, Option)> { + let (res, unresolved, imp) = self.resolve_path_in_type_ns(db, path)?; + if unresolved.is_some() { + return None; + } + Some((res, imp)) + } + pub fn resolve_path_in_type_ns_fully( &self, db: &dyn DefDatabase, path: &Path, ) -> Option { - let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?; + let (res, unresolved, _) = self.resolve_path_in_type_ns(db, path)?; if unresolved.is_some() { return None; } @@ -263,7 +249,6 @@ impl Resolver { RawVisibility::Public => Some(Visibility::Public), } } - pub fn resolve_path_in_value_ns( &self, db: &dyn DefDatabase, @@ -272,17 +257,20 @@ impl Resolver { let path = match path { Path::Normal { mod_path, .. } => mod_path, Path::LangItem(l) => { - return Some(ResolveValueResult::ValueNs(match *l { - LangItemTarget::Function(it) => ValueNs::FunctionId(it), - LangItemTarget::Static(it) => ValueNs::StaticId(it), - LangItemTarget::Struct(it) => ValueNs::StructId(it), - LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it), - LangItemTarget::Union(_) - | LangItemTarget::ImplDef(_) - | LangItemTarget::TypeAlias(_) - | LangItemTarget::Trait(_) - | LangItemTarget::EnumId(_) => return None, - })) + return Some(ResolveValueResult::ValueNs( + match *l { + LangItemTarget::Function(it) => ValueNs::FunctionId(it), + LangItemTarget::Static(it) => ValueNs::StaticId(it), + LangItemTarget::Struct(it) => ValueNs::StructId(it), + LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it), + LangItemTarget::Union(_) + | LangItemTarget::ImplDef(_) + | LangItemTarget::TypeAlias(_) + | LangItemTarget::Trait(_) + | LangItemTarget::EnumId(_) => return None, + }, + None, + )) } }; let n_segments = path.segments().len(); @@ -304,20 +292,24 @@ impl Resolver { .find(|entry| entry.name() == first_name); if let Some(e) = entry { - return Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding( - e.binding(), - ))); + return Some(ResolveValueResult::ValueNs( + ValueNs::LocalBinding(e.binding()), + None, + )); } } Scope::GenericParams { params, def } => { if let Some(id) = params.find_const_by_name(first_name, *def) { let val = ValueNs::GenericParam(id); - return Some(ResolveValueResult::ValueNs(val)); + return Some(ResolveValueResult::ValueNs(val, None)); } } &Scope::ImplDefScope(impl_) => { if first_name == &name![Self] { - return Some(ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_))); + return Some(ResolveValueResult::ValueNs( + ValueNs::ImplSelf(impl_), + None, + )); } } // bare `Self` doesn't work in the value namespace in a struct/enum definition @@ -336,18 +328,22 @@ impl Resolver { Scope::GenericParams { params, def } => { if let Some(id) = params.find_type_by_name(first_name, *def) { let ty = TypeNs::GenericParam(id); - return Some(ResolveValueResult::Partial(ty, 1)); + return Some(ResolveValueResult::Partial(ty, 1, None)); } } &Scope::ImplDefScope(impl_) => { if first_name == &name![Self] { - return Some(ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1)); + return Some(ResolveValueResult::Partial( + TypeNs::SelfType(impl_), + 1, + None, + )); } } Scope::AdtScope(adt) => { if first_name == &name![Self] { let ty = TypeNs::AdtSelfType(*adt); - return Some(ResolveValueResult::Partial(ty, 1)); + return Some(ResolveValueResult::Partial(ty, 1, None)); } } Scope::BlockScope(m) => { @@ -368,7 +364,7 @@ impl Resolver { // `use core::u16;`. if path.kind == PathKind::Plain && n_segments > 1 { if let Some(builtin) = BuiltinType::by_name(first_name) { - return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1)); + return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None)); } } @@ -381,7 +377,7 @@ impl Resolver { path: &Path, ) -> Option { match self.resolve_path_in_value_ns(db, path)? { - ResolveValueResult::ValueNs(it) => Some(it), + ResolveValueResult::ValueNs(it, _) => Some(it), ResolveValueResult::Partial(..) => None, } } @@ -391,12 +387,12 @@ impl Resolver { db: &dyn DefDatabase, path: &ModPath, expected_macro_kind: Option, - ) -> Option { + ) -> Option<(MacroId, Option)> { let (item_map, module) = self.item_scope(); item_map .resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind) .0 - .take_macros() + .take_macros_import() } /// Returns a set of names available in the current scope. @@ -456,21 +452,22 @@ impl Resolver { def_map[module_id].scope.entries().for_each(|(name, def)| { res.add_per_ns(name, def); }); + def_map[module_id].scope.legacy_macros().for_each(|(name, macs)| { macs.iter().for_each(|&mac| { res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac))); }) }); - def_map.macro_use_prelude().for_each(|(name, def)| { + def_map.macro_use_prelude().for_each(|(name, (def, _extern_crate))| { res.add(name, ScopeDef::ModuleDef(def.into())); }); - def_map.extern_prelude().for_each(|(name, def)| { - res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def))); + def_map.extern_prelude().for_each(|(name, (def, _extern_crate))| { + res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def.into()))); }); BUILTIN_SCOPE.iter().for_each(|(name, &def)| { res.add_per_ns(name, def); }); - if let Some(prelude) = def_map.prelude() { + if let Some((prelude, _use)) = def_map.prelude() { let prelude_def_map = prelude.def_map(db); for (name, def) in prelude_def_map[prelude.local_id].scope.entries() { res.add_per_ns(name, def) @@ -479,6 +476,23 @@ impl Resolver { res.map } + pub fn extern_crate_decls_in_scope<'a>( + &'a self, + db: &'a dyn DefDatabase, + ) -> impl Iterator + 'a { + self.module_scope.def_map[self.module_scope.module_id] + .scope + .extern_crate_decls() + .map(|id| ExternCrateDeclData::extern_crate_decl_data_query(db, id).name.clone()) + } + + pub fn extern_crates_in_scope<'a>(&'a self) -> impl Iterator + 'a { + self.module_scope + .def_map + .extern_prelude() + .map(|(name, module_id)| (name.clone(), module_id.0.into())) + } + pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet { // FIXME(trait_alias): Trait alias brings aliased traits in scope! Note that supertraits of // aliased traits are NOT brought in scope (unless also aliased). @@ -501,7 +515,7 @@ impl Resolver { } // Fill in the prelude traits - if let Some(prelude) = self.module_scope.def_map.prelude() { + if let Some((prelude, _use)) = self.module_scope.def_map.prelude() { let prelude_def_map = prelude.def_map(db); traits.extend(prelude_def_map[prelude.local_id].scope.traits()); } @@ -804,11 +818,12 @@ impl ModuleItemMap { self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other); match idx { None => { - let value = to_value_ns(module_def)?; - Some(ResolveValueResult::ValueNs(value)) + let (value, import) = to_value_ns(module_def)?; + Some(ResolveValueResult::ValueNs(value, import)) } Some(idx) => { - let ty = match module_def.take_types()? { + let (def, _, import) = module_def.take_types_full()?; + let ty = match def { ModuleDefId::AdtId(it) => TypeNs::AdtId(it), ModuleDefId::TraitId(it) => TypeNs::TraitId(it), ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it), @@ -822,7 +837,7 @@ impl ModuleItemMap { | ModuleDefId::MacroId(_) | ModuleDefId::StaticId(_) => return None, }; - Some(ResolveValueResult::Partial(ty, idx)) + Some(ResolveValueResult::Partial(ty, idx, import)) } } } @@ -831,16 +846,17 @@ impl ModuleItemMap { &self, db: &dyn DefDatabase, path: &ModPath, - ) -> Option<(TypeNs, Option)> { + ) -> Option<(TypeNs, Option, Option)> { let (module_def, idx) = self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other); - let res = to_type_ns(module_def)?; - Some((res, idx)) + let (res, import) = to_type_ns(module_def)?; + Some((res, idx, import)) } } -fn to_value_ns(per_ns: PerNs) -> Option { - let res = match per_ns.take_values()? { +fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option)> { + let (def, import) = per_ns.take_values_import()?; + let res = match def { ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it), ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it), ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariantId(it), @@ -855,11 +871,12 @@ fn to_value_ns(per_ns: PerNs) -> Option { | ModuleDefId::MacroId(_) | ModuleDefId::ModuleId(_) => return None, }; - Some(res) + Some((res, import)) } -fn to_type_ns(per_ns: PerNs) -> Option { - let res = match per_ns.take_types()? { +fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option)> { + let (def, _, import) = per_ns.take_types_full()?; + let res = match def { ModuleDefId::AdtId(it) => TypeNs::AdtId(it), ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it), @@ -875,7 +892,7 @@ fn to_type_ns(per_ns: PerNs) -> Option { | ModuleDefId::StaticId(_) | ModuleDefId::ModuleId(_) => return None, }; - Some(res) + Some((res, import)) } type FxIndexMap = IndexMap>; @@ -892,13 +909,13 @@ impl ScopeNames { } } fn add_per_ns(&mut self, name: &Name, def: PerNs) { - if let &Some((ty, _)) = &def.types { + if let &Some((ty, _, _)) = &def.types { self.add(name, ScopeDef::ModuleDef(ty)) } - if let &Some((def, _)) = &def.values { + if let &Some((def, _, _)) = &def.values { self.add(name, ScopeDef::ModuleDef(def)) } - if let &Some((mac, _)) = &def.macros { + if let &Some((mac, _, _)) = &def.macros { self.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac))) } if def.is_none() { diff --git a/crates/hir-def/src/src.rs b/crates/hir-def/src/src.rs index 6047f770d4d..3770103cda5 100644 --- a/crates/hir-def/src/src.rs +++ b/crates/hir-def/src/src.rs @@ -5,8 +5,8 @@ use la_arena::ArenaMap; use syntax::ast; use crate::{ - db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc, Macro2Loc, MacroRulesLoc, - ProcMacroLoc, + db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc, Lookup, Macro2Loc, + MacroRulesLoc, ProcMacroLoc, UseId, }; pub trait HasSource { @@ -83,3 +83,18 @@ pub trait HasChildSource { type Value; fn child_source(&self, db: &dyn DefDatabase) -> InFile>; } + +impl HasChildSource> for UseId { + type Value = ast::UseTree; + fn child_source( + &self, + db: &dyn DefDatabase, + ) -> InFile, Self::Value>> { + let loc = &self.lookup(db); + let use_ = &loc.id.item_tree(db)[loc.id.value]; + InFile::new( + loc.id.file_id(), + use_.use_tree_source_map(db, loc.id.file_id()).into_iter().collect(), + ) + } +} diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs index 4c918e55b92..0ec2422b30c 100644 --- a/crates/hir-expand/src/attrs.rs +++ b/crates/hir-expand/src/attrs.rs @@ -342,14 +342,7 @@ fn inner_attributes( ast::Impl(it) => it.assoc_item_list()?.syntax().clone(), ast::Module(it) => it.item_list()?.syntax().clone(), ast::BlockExpr(it) => { - use syntax::SyntaxKind::{BLOCK_EXPR , EXPR_STMT}; - // Block expressions accept outer and inner attributes, but only when they are the outer - // expression of an expression statement or the final expression of another block expression. - let may_carry_attributes = matches!( - it.syntax().parent().map(|it| it.kind()), - Some(BLOCK_EXPR | EXPR_STMT) - ); - if !may_carry_attributes { + if !it.may_carry_attributes() { return None } syntax.clone() diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 1f1e20f49e3..4be55126b86 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -37,7 +37,7 @@ use either::Either; use syntax::{ algo::{self, skip_trivia_token}, ast::{self, AstNode, HasDocComments}, - AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, + AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize, }; use crate::{ @@ -544,7 +544,7 @@ impl MacroCallKind { }; let range = match kind { - MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(), + MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(), MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { // FIXME: should be the range of the macro name, not the whole derive // FIXME: handle `cfg_attr` @@ -642,6 +642,8 @@ impl ExpansionInfo { db: &dyn db::ExpandDatabase, item: Option, token: InFile<&SyntaxToken>, + // FIXME: use this for range mapping, so that we can resolve inline format args + _relative_token_offset: Option, ) -> Option> + '_> { assert_eq!(token.file_id, self.arg.file_id); let token_id_in_attr_input = if let Some(item) = item { @@ -840,9 +842,6 @@ impl AstId { pub type ErasedAstId = InFile; impl ErasedAstId { - pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode { - self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)) - } pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> SyntaxNodePtr { db.ast_id_map(self.file_id).get_raw(self.value) } @@ -1054,16 +1053,6 @@ impl InFile { } } } - - pub fn ancestors_with_macros( - self, - db: &dyn db::ExpandDatabase, - ) -> impl Iterator> + '_ { - self.value.parent().into_iter().flat_map({ - let file_id = self.file_id; - move |parent| InFile::new(file_id, &parent).ancestors_with_macros(db) - }) - } } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] diff --git a/crates/hir-ty/src/builder.rs b/crates/hir-ty/src/builder.rs index eec57ba3f80..967e028bfb1 100644 --- a/crates/hir-ty/src/builder.rs +++ b/crates/hir-ty/src/builder.rs @@ -17,7 +17,8 @@ use smallvec::SmallVec; use crate::{ consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig, - GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind, + GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, + TyKind, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -79,9 +80,9 @@ impl TyBuilder { let expected_kind = &self.param_kinds[self.vec.len()]; let arg_kind = match arg.data(Interner) { - chalk_ir::GenericArgData::Ty(_) => ParamKind::Type, - chalk_ir::GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"), - chalk_ir::GenericArgData::Const(c) => { + GenericArgData::Ty(_) => ParamKind::Type, + GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"), + GenericArgData::Const(c) => { let c = c.data(Interner); ParamKind::Const(c.ty.clone()) } @@ -139,8 +140,8 @@ impl TyBuilder { fn assert_match_kind(&self, a: &chalk_ir::GenericArg, e: &ParamKind) { match (a.data(Interner), e) { - (chalk_ir::GenericArgData::Ty(_), ParamKind::Type) - | (chalk_ir::GenericArgData::Const(_), ParamKind::Const(_)) => (), + (GenericArgData::Ty(_), ParamKind::Type) + | (GenericArgData::Const(_), ParamKind::Const(_)) => (), _ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds), } } diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 1c0f7b08da8..0348680e5da 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -1,7 +1,7 @@ //! Constant evaluation details use base_db::CrateId; -use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData}; +use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex}; use hir_def::{ hir::Expr, path::Path, @@ -120,7 +120,7 @@ pub fn unknown_const(ty: Ty) -> Const { } pub fn unknown_const_as_generic(ty: Ty) -> GenericArg { - GenericArgData::Const(unknown_const(ty)).intern(Interner) + unknown_const(ty).cast(Interner) } /// Interns a constant scalar with the given type diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 666955fa1c3..7ad3659a4f6 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -1203,6 +1203,27 @@ fn destructing_assignment() { "#, 5, ); + check_number( + r#" + const GOAL: u8 = { + let (mut a, mut b) = (2, 5); + (a, b) = (b, a); + a * 10 + b + }; + "#, + 52, + ); + check_number( + r#" + struct Point { x: i32, y: i32 } + const GOAL: i32 = { + let mut p = Point { x: 5, y: 6 }; + (p.x, _) = (p.y, p.x); + p.x * 10 + p.y + }; + "#, + 66, + ); } #[test] @@ -1432,6 +1453,30 @@ fn from_trait() { ); } +#[test] +fn closure_clone() { + check_number( + r#" +//- minicore: clone, fn +struct S(u8); + +impl Clone for S(u8) { + fn clone(&self) -> S { + S(self.0 + 5) + } +} + +const GOAL: u8 = { + let s = S(3); + let cl = move || s; + let cl = cl.clone(); + cl().0 +} + "#, + 8, + ); +} + #[test] fn builtin_derive_macro() { check_number( @@ -2396,14 +2441,14 @@ fn const_loop() { fn const_transfer_memory() { check_number( r#" - //- minicore: slice, index, coerce_unsized + //- minicore: slice, index, coerce_unsized, option const A1: &i32 = &1; const A2: &i32 = &10; const A3: [&i32; 3] = [&1, &2, &100]; - const A4: (i32, &i32) = (1, &1000); - const GOAL: i32 = *A1 + *A2 + *A3[2] + *A4.1; + const A4: (i32, &i32, Option<&i32>) = (1, &1000, Some(&10000)); + const GOAL: i32 = *A1 + *A2 + *A3[2] + *A4.1 + *A4.2.unwrap_or(&5); "#, - 1111, + 11111, ); } diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs index 9f9a56ffab0..cbca0e801d4 100644 --- a/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -75,7 +75,7 @@ fn walk_unsafe( Expr::Path(path) => { let resolver = resolver_for_expr(db.upcast(), def, current); let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path); - if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial { + if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial { if db.static_data(id).mutable { unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); } diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 1b4ee4613d6..f6d6b00d740 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -1809,6 +1809,25 @@ impl HirDisplay for Path { } } + // Convert trait's `Self` bound back to the surface syntax. Note there is no associated + // trait, so there can only be one path segment that `has_self_type`. The `Self` type + // itself can contain further qualified path through, which will be handled by recursive + // `hir_fmt`s. + // + // `trait_mod::Trait::Assoc` + // => + // `>::Assoc` + let trait_self_ty = self.segments().iter().find_map(|seg| { + let generic_args = seg.args_and_bindings?; + generic_args.has_self_type.then(|| &generic_args.args[0]) + }); + if let Some(ty) = trait_self_ty { + write!(f, "<")?; + ty.hir_fmt(f)?; + write!(f, " as ")?; + // Now format the path of the trait... + } + for (seg_idx, segment) in self.segments().iter().enumerate() { if !matches!(self.kind(), PathKind::Plain) || seg_idx > 0 { write!(f, "::")?; @@ -1840,15 +1859,12 @@ impl HirDisplay for Path { return Ok(()); } - write!(f, "<")?; let mut first = true; - for arg in generic_args.args.iter() { + // Skip the `Self` bound if exists. It's handled outside the loop. + for arg in &generic_args.args[generic_args.has_self_type as usize..] { if first { first = false; - if generic_args.has_self_type { - // FIXME: Convert to `` form. - write!(f, "Self = ")?; - } + write!(f, "<")?; } else { write!(f, ", ")?; } @@ -1857,6 +1873,7 @@ impl HirDisplay for Path { for binding in generic_args.bindings.iter() { if first { first = false; + write!(f, "<")?; } else { write!(f, ", ")?; } @@ -1872,9 +1889,20 @@ impl HirDisplay for Path { } } } - write!(f, ">")?; + + // There may be no generic arguments to print, in case of a trait having only a + // single `Self` bound which is converted to `::Assoc`. + if !first { + write!(f, ">")?; + } + + // Current position: `|` + if generic_args.has_self_type { + write!(f, ">")?; + } } } + Ok(()) } } diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index b4915dbf0f9..0fb4934444b 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -1017,7 +1017,7 @@ impl<'a> InferenceContext<'a> { let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into()); let (resolution, unresolved) = if value_ns { match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) { - Some(ResolveValueResult::ValueNs(value)) => match value { + Some(ResolveValueResult::ValueNs(value, _)) => match value { ValueNs::EnumVariantId(var) => { let substs = ctx.substs_from_path(path, var.into(), true); let ty = self.db.ty(var.parent.into()); @@ -1033,12 +1033,14 @@ impl<'a> InferenceContext<'a> { ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None), _ => return (self.err_ty(), None), }, - Some(ResolveValueResult::Partial(typens, unresolved)) => (typens, Some(unresolved)), + Some(ResolveValueResult::Partial(typens, unresolved, _)) => { + (typens, Some(unresolved)) + } None => return (self.err_ty(), None), } } else { match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { - Some(it) => it, + Some((it, idx, _)) => (it, idx), None => return (self.err_ty(), None), } }; diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 1781f6c58f1..23efe616f4f 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -322,7 +322,7 @@ impl InferenceContext<'_> { Expr::Path(p) => { let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr); if let Some(r) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) { - if let ResolveValueResult::ValueNs(v) = r { + if let ResolveValueResult::ValueNs(v, _) = r { if let ValueNs::LocalBinding(b) = v { return Some(HirPlace { local: b, projections: vec![] }); } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 8cbdae62526..8b352141084 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -5,9 +5,7 @@ use std::{ mem, }; -use chalk_ir::{ - cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind, -}; +use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind}; use hir_def::{ generics::TypeOrConstParamData, hir::{ @@ -750,7 +748,7 @@ impl InferenceContext<'_> { self.resolve_associated_type_with_params( self_ty, self.resolve_ops_index_output(), - &[GenericArgData::Ty(index_ty).intern(Interner)], + &[index_ty.cast(Interner)], ) } else { self.err_ty() @@ -1721,16 +1719,13 @@ impl InferenceContext<'_> { for (id, data) in def_generics.iter().skip(substs.len()) { match data { TypeOrConstParamData::TypeParamData(_) => { - substs.push(GenericArgData::Ty(self.table.new_type_var()).intern(Interner)) - } - TypeOrConstParamData::ConstParamData(_) => { - substs.push( - GenericArgData::Const(self.table.new_const_var( - self.db.const_param_ty(ConstParamId::from_unchecked(id)), - )) - .intern(Interner), - ) + substs.push(self.table.new_type_var().cast(Interner)) } + TypeOrConstParamData::ConstParamData(_) => substs.push( + self.table + .new_const_var(self.db.const_param_ty(ConstParamId::from_unchecked(id))) + .cast(Interner), + ), } } assert_eq!(substs.len(), total_len); diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 79d9e21e797..2a51c84db3a 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -61,8 +61,8 @@ impl InferenceContext<'_> { self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?; match value_or_partial { - ResolveValueResult::ValueNs(it) => (it, None), - ResolveValueResult::Partial(def, remaining_index) => self + ResolveValueResult::ValueNs(it, _) => (it, None), + ResolveValueResult::Partial(def, remaining_index, _) => self .resolve_assoc_item(def, path, remaining_index, id) .map(|(it, substs)| (it, Some(substs)))?, } diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 0fb71135b4d..0a68a9f3b58 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -10,7 +10,6 @@ use chalk_solve::infer::ParameterEnaVariableExt; use either::Either; use ena::unify::UnifyKey; use hir_expand::name; -use stdx::never; use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; @@ -92,15 +91,10 @@ pub(crate) fn unify( let vars = Substitution::from_iter( Interner, tys.binders.iter(Interner).map(|it| match &it.kind { - chalk_ir::VariableKind::Ty(_) => { - GenericArgData::Ty(table.new_type_var()).intern(Interner) - } - chalk_ir::VariableKind::Lifetime => { - GenericArgData::Ty(table.new_type_var()).intern(Interner) - } // FIXME: maybe wrong? - chalk_ir::VariableKind::Const(ty) => { - GenericArgData::Const(table.new_const_var(ty.clone())).intern(Interner) - } + chalk_ir::VariableKind::Ty(_) => table.new_type_var().cast(Interner), + // FIXME: maybe wrong? + chalk_ir::VariableKind::Lifetime => table.new_type_var().cast(Interner), + chalk_ir::VariableKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner), }), ); let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); @@ -111,10 +105,10 @@ pub(crate) fn unify( // default any type vars that weren't unified back to their original bound vars // (kind of hacky) let find_var = |iv| { - vars.iter(Interner).position(|v| match v.interned() { - chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner), - chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner), - chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner), + vars.iter(Interner).position(|v| match v.data(Interner) { + GenericArgData::Ty(ty) => ty.inference_var(Interner), + GenericArgData::Lifetime(lt) => lt.inference_var(Interner), + GenericArgData::Const(c) => c.inference_var(Interner), } == Some(iv)) }; let fallback = |iv, kind, default, binder| match kind { @@ -149,6 +143,9 @@ pub(crate) struct InferenceTable<'a> { var_unification_table: ChalkInferenceTable, type_variable_table: Vec, pending_obligations: Vec>>, + /// Double buffer used in [`Self::resolve_obligations_as_possible`] to cut down on + /// temporary allocations. + resolve_obligations_buffer: Vec>>, } pub(crate) struct InferenceTableSnapshot { @@ -165,6 +162,7 @@ impl<'a> InferenceTable<'a> { var_unification_table: ChalkInferenceTable::new(), type_variable_table: Vec::new(), pending_obligations: Vec::new(), + resolve_obligations_buffer: Vec::new(), } } @@ -516,10 +514,10 @@ impl<'a> InferenceTable<'a> { pub(crate) fn resolve_obligations_as_possible(&mut self) { let _span = profile::span("resolve_obligations_as_possible"); let mut changed = true; - let mut obligations = Vec::new(); - while changed { - changed = false; + let mut obligations = mem::take(&mut self.resolve_obligations_buffer); + while mem::take(&mut changed) { mem::swap(&mut self.pending_obligations, &mut obligations); + for canonicalized in obligations.drain(..) { if !self.check_changed(&canonicalized) { self.pending_obligations.push(canonicalized); @@ -534,6 +532,8 @@ impl<'a> InferenceTable<'a> { self.register_obligation_in_env(uncanonical); } } + self.resolve_obligations_buffer = obligations; + self.resolve_obligations_buffer.clear(); } pub(crate) fn fudge_inference>( @@ -611,9 +611,9 @@ impl<'a> InferenceTable<'a> { fn check_changed(&mut self, canonicalized: &Canonicalized>) -> bool { canonicalized.free_vars.iter().any(|var| { let iv = match var.data(Interner) { - chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner), - chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner), - chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner), + GenericArgData::Ty(ty) => ty.inference_var(Interner), + GenericArgData::Lifetime(lt) => lt.inference_var(Interner), + GenericArgData::Const(c) => c.inference_var(Interner), } .expect("free var is not inference var"); if self.var_unification_table.probe_var(iv).is_some() { @@ -690,14 +690,10 @@ impl<'a> InferenceTable<'a> { .fill(|it| { let arg = match it { ParamKind::Type => self.new_type_var(), - ParamKind::Const(ty) => { - never!("Tuple with const parameter"); - return GenericArgData::Const(self.new_const_var(ty.clone())) - .intern(Interner); - } + ParamKind::Const(_) => unreachable!("Tuple with const parameter"), }; arg_tys.push(arg.clone()); - GenericArgData::Ty(arg).intern(Interner) + arg.cast(Interner) }) .build(); diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index b3ca2a22258..405bb001b5d 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -52,12 +52,14 @@ use hir_expand::name; use la_arena::{Arena, Idx}; use mir::{MirEvalError, VTableMap}; use rustc_hash::FxHashSet; +use syntax::ast::{make, ConstArg}; use traits::FnTrait; use triomphe::Arc; use utils::Generics; use crate::{ - consteval::unknown_const, db::HirDatabase, infer::unify::InferenceTable, utils::generics, + consteval::unknown_const, db::HirDatabase, display::HirDisplay, infer::unify::InferenceTable, + utils::generics, }; pub use autoderef::autoderef; @@ -719,3 +721,16 @@ where value.visit_with(&mut collector, DebruijnIndex::INNERMOST); collector.placeholders.into_iter().collect() } + +pub fn known_const_to_ast(konst: &Const, db: &dyn HirDatabase) -> Option { + if let ConstValue::Concrete(c) = &konst.interned().value { + match c.interned { + ConstScalar::UnevaluatedConst(GeneralConstId::InTypeConstId(cid), _) => { + return Some(cid.source(db.upcast())); + } + ConstScalar::Unknown => return None, + _ => (), + } + } + Some(make::expr_const_value(konst.display(db).to_string().as_str())) +} diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 2837f400bce..9a61f153599 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -58,10 +58,9 @@ use crate::{ InTypeConstIdMetadata, }, AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, - FnPointer, FnSig, FnSubst, GenericArgData, ImplTraitId, Interner, ParamKind, PolyFnSig, - ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, - ReturnTypeImplTraits, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, - TyKind, WhereClause, + FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy, + QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, + Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, }; #[derive(Debug)] @@ -213,6 +212,19 @@ impl<'a> TyLoweringContext<'a> { self.lower_ty_ext(type_ref).0 } + pub fn lower_const(&self, const_ref: &ConstRef, const_type: Ty) -> Const { + const_or_path_to_chalk( + self.db, + self.resolver, + self.owner, + const_type, + const_ref, + self.type_param_mode, + || self.generics(), + self.in_binders, + ) + } + fn generics(&self) -> Generics { generics( self.db.upcast(), @@ -242,17 +254,7 @@ impl<'a> TyLoweringContext<'a> { } TypeRef::Array(inner, len) => { let inner_ty = self.lower_ty(inner); - let const_len = const_or_path_to_chalk( - self.db, - self.resolver, - self.owner, - TyBuilder::usize(), - len, - self.type_param_mode, - || self.generics(), - self.in_binders, - ); - + let const_len = self.lower_const(len, TyBuilder::usize()); TyKind::Array(inner_ty, const_len).intern(Interner) } TypeRef::Slice(inner) => { @@ -391,11 +393,9 @@ impl<'a> TyLoweringContext<'a> { let ty = { let macro_call = macro_call.to_node(self.db.upcast()); let resolver = |path| { - self.resolver.resolve_path_as_macro( - self.db.upcast(), - &path, - Some(MacroSubNs::Bang), - ) + self.resolver + .resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang)) + .map(|(it, _)| it) }; match expander.enter_expand::(self.db.upcast(), macro_call, resolver) { @@ -447,7 +447,7 @@ impl<'a> TyLoweringContext<'a> { return None; } let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { - Some((it, None)) => it, + Some((it, None, _)) => it, _ => return None, }; match resolution { @@ -627,7 +627,7 @@ impl<'a> TyLoweringContext<'a> { return self.lower_ty_relative_path(ty, res, path.segments()); } - let (resolution, remaining_index) = + let (resolution, remaining_index, _) = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { Some(it) => it, None => return (TyKind::Error.intern(Interner), None), @@ -847,18 +847,7 @@ impl<'a> TyLoweringContext<'a> { arg, &mut (), |_, type_ref| self.lower_ty(type_ref), - |_, c, ty| { - const_or_path_to_chalk( - self.db, - self.resolver, - self.owner, - ty, - c, - self.type_param_mode, - || self.generics(), - self.in_binders, - ) - }, + |_, const_ref, ty| self.lower_const(const_ref, ty), ) { had_explicit_args = true; substs.push(x); @@ -1604,24 +1593,35 @@ pub(crate) fn generic_defaults_query( .iter() .enumerate() .map(|(idx, (id, p))| { - let p = match p { - TypeOrConstParamData::TypeParamData(p) => p, - TypeOrConstParamData::ConstParamData(_) => { - // FIXME: implement const generic defaults - let val = unknown_const_as_generic( - db.const_param_ty(ConstParamId::from_unchecked(id)), - ); - return make_binders(db, &generic_params, val); + match p { + TypeOrConstParamData::TypeParamData(p) => { + let mut ty = p + .default + .as_ref() + .map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t)); + // Each default can only refer to previous parameters. + // Type variable default referring to parameter coming + // after it is forbidden (FIXME: report diagnostic) + ty = fallback_bound_vars(ty, idx, parent_start_idx); + crate::make_binders(db, &generic_params, ty.cast(Interner)) } - }; - let mut ty = - p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t)); - - // Each default can only refer to previous parameters. - // Type variable default referring to parameter coming - // after it is forbidden (FIXME: report diagnostic) - ty = fallback_bound_vars(ty, idx, parent_start_idx); - crate::make_binders(db, &generic_params, ty.cast(Interner)) + TypeOrConstParamData::ConstParamData(p) => { + let mut val = p.default.as_ref().map_or_else( + || { + unknown_const_as_generic( + db.const_param_ty(ConstParamId::from_unchecked(id)), + ) + }, + |c| { + let c = ctx.lower_const(c, ctx.lower_ty(&p.ty)); + c.cast(Interner) + }, + ); + // Each default can only refer to previous parameters, see above. + val = fallback_bound_vars(val, idx, parent_start_idx); + make_binders(db, &generic_params, val) + } + } }) // FIXME: use `Arc::from_iter` when it becomes available .collect::>(), @@ -1643,9 +1643,7 @@ pub(crate) fn generic_defaults_recover( .iter_id() .map(|id| { let val = match id { - Either::Left(_) => { - GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner) - } + Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)), }; crate::make_binders(db, &generic_params, val) @@ -1991,16 +1989,9 @@ pub(crate) fn generic_arg_to_chalk<'a, T>( } }; Some(match (arg, kind) { - (GenericArg::Type(type_ref), ParamKind::Type) => { - let ty = for_type(this, type_ref); - GenericArgData::Ty(ty).intern(Interner) - } - (GenericArg::Const(c), ParamKind::Const(c_ty)) => { - GenericArgData::Const(for_const(this, c, c_ty)).intern(Interner) - } - (GenericArg::Const(_), ParamKind::Type) => { - GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner) - } + (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, type_ref).cast(Interner), + (GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner), + (GenericArg::Const(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner), (GenericArg::Type(t), ParamKind::Const(c_ty)) => { // We want to recover simple idents, which parser detects them // as types. Maybe here is not the best place to do it, but @@ -2010,9 +2001,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>( if p.kind == PathKind::Plain { if let [n] = p.segments() { let c = ConstRef::Path(n.clone()); - return Some( - GenericArgData::Const(for_const(this, &c, c_ty)).intern(Interner), - ); + return Some(for_const(this, &c, c_ty).cast(Interner)); } } } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 9e30eed56f3..3944feb128c 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -10,7 +10,7 @@ use std::{ }; use base_db::{CrateId, FileId}; -use chalk_ir::Mutability; +use chalk_ir::{cast::Cast, Mutability}; use either::Either; use hir_def::{ builtin_type::BuiltinType, @@ -40,8 +40,8 @@ use crate::{ name, static_lifetime, traits::FnTrait, utils::{detect_variant_from_bytes, ClosureSubst}, - CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, + CallableDefId, ClosureId, Const, ConstScalar, FnDefId, Interner, MemoryMap, Substitution, + TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, }; use super::{ @@ -2007,7 +2007,28 @@ impl Evaluator<'_> { } } AdtId::UnionId(_) => (), - AdtId::EnumId(_) => (), + AdtId::EnumId(e) => { + if let Some((variant, layout)) = detect_variant_from_bytes( + &layout, + self.db, + self.trait_env.clone(), + self.read_memory(addr, layout.size.bytes_usize())?, + e, + ) { + let ev = EnumVariantId { parent: e, local_id: variant }; + for (i, (_, ty)) in self.db.field_types(ev.into()).iter().enumerate() { + let offset = layout.fields.offset(i).bytes_usize(); + let ty = ty.clone().substitute(Interner, subst); + self.patch_addresses( + patch_map, + old_vtable, + addr.offset(offset), + &ty, + locals, + )?; + } + } + } }, TyKind::Tuple(_, subst) => { for (id, ty) in subst.iter(Interner).enumerate() { @@ -2248,7 +2269,7 @@ impl Evaluator<'_> { interval: args_for_target[0].interval.slice(0..self.ptr_size()), ty: ty.clone(), }; - let ty = GenericArgData::Ty(ty.clone()).intern(Interner); + let ty = ty.clone().cast(Interner); let generics_for_target = Substitution::from_iter( Interner, generic_args.iter(Interner).enumerate().map(|(i, it)| { diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index b2e29fd34b5..52943e97ac0 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -136,7 +136,10 @@ impl Evaluator<'_> { not_supported!("wrong generic arg kind for clone"); }; // Clone has special impls for tuples and function pointers - if matches!(self_ty.kind(Interner), TyKind::Function(_) | TyKind::Tuple(..)) { + if matches!( + self_ty.kind(Interner), + TyKind::Function(_) | TyKind::Tuple(..) | TyKind::Closure(..) + ) { self.exec_clone(def, args, self_ty.clone(), locals, destination, span)?; return Ok(true); } @@ -167,32 +170,26 @@ impl Evaluator<'_> { return destination .write_from_interval(self, Interval { addr, size: destination.size }); } + TyKind::Closure(id, subst) => { + let [arg] = args else { + not_supported!("wrong arg count for clone"); + }; + let addr = Address::from_bytes(arg.get(self)?)?; + let (closure_owner, _) = self.db.lookup_intern_closure((*id).into()); + let infer = self.db.infer(closure_owner); + let (captures, _) = infer.closure_info(id); + let layout = self.layout(&self_ty)?; + let ty_iter = captures.iter().map(|c| c.ty(subst)); + self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?; + } TyKind::Tuple(_, subst) => { let [arg] = args else { not_supported!("wrong arg count for clone"); }; let addr = Address::from_bytes(arg.get(self)?)?; let layout = self.layout(&self_ty)?; - for (i, ty) in subst.iter(Interner).enumerate() { - let ty = ty.assert_ty_ref(Interner); - let size = self.layout(ty)?.size.bytes_usize(); - let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?; - let arg = IntervalAndTy { - interval: Interval { addr: tmp, size: self.ptr_size() }, - ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone()) - .intern(Interner), - }; - let offset = layout.fields.offset(i).bytes_usize(); - self.write_memory(tmp, &addr.offset(offset).to_bytes())?; - self.exec_clone( - def, - &[arg], - ty.clone(), - locals, - destination.slice(offset..offset + size), - span, - )?; - } + let ty_iter = subst.iter(Interner).map(|ga| ga.assert_ty_ref(Interner).clone()); + self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?; } _ => { self.exec_fn_with_args( @@ -209,6 +206,37 @@ impl Evaluator<'_> { Ok(()) } + fn exec_clone_for_fields( + &mut self, + ty_iter: impl Iterator, + layout: Arc, + addr: Address, + def: FunctionId, + locals: &Locals, + destination: Interval, + span: MirSpan, + ) -> Result<()> { + for (i, ty) in ty_iter.enumerate() { + let size = self.layout(&ty)?.size.bytes_usize(); + let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?; + let arg = IntervalAndTy { + interval: Interval { addr: tmp, size: self.ptr_size() }, + ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone()).intern(Interner), + }; + let offset = layout.fields.offset(i).bytes_usize(); + self.write_memory(tmp, &addr.offset(offset).to_bytes())?; + self.exec_clone( + def, + &[arg], + ty, + locals, + destination.slice(offset..offset + size), + span, + )?; + } + Ok(()) + } + fn exec_alloc_fn( &mut self, alloc_fn: &str, @@ -473,6 +501,38 @@ impl Evaluator<'_> { self.write_memory_using_ref(destination.addr, destination.size)?.fill(0); Ok(()) } + "getenv" => { + let [name] = args else { + return Err(MirEvalError::TypeError("libc::write args are not provided")); + }; + let mut name_buf = vec![]; + let name = { + let mut index = Address::from_bytes(name.get(self)?)?; + loop { + let byte = self.read_memory(index, 1)?[0]; + index = index.offset(1); + if byte == 0 { + break; + } + name_buf.push(byte); + } + String::from_utf8_lossy(&name_buf) + }; + let value = self.db.crate_graph()[self.crate_id].env.get(&name); + match value { + None => { + // Write null as fail + self.write_memory_using_ref(destination.addr, destination.size)?.fill(0); + } + Some(mut value) => { + value.push('\0'); + let addr = self.heap_allocate(value.len(), 1)?; + self.write_memory(addr, value.as_bytes())?; + self.write_memory(destination.addr, &addr.to_bytes())?; + } + } + Ok(()) + } _ => not_supported!("unknown external function {as_str}"), } } diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 46165cf3d69..ff30dc6dade 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -729,6 +729,48 @@ fn main() { ) } +#[test] +fn posix_getenv() { + check_pass( + r#" +//- /main.rs env:foo=bar + +type c_char = u8; + +extern "C" { + pub fn getenv(s: *const c_char) -> *mut c_char; +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let result = getenv(b"foo\0" as *const _); + if *result != b'b' { + should_not_reach(); + } + let result = (result as usize + 1) as *const c_char; + if *result != b'a' { + should_not_reach(); + } + let result = (result as usize + 1) as *const c_char; + if *result != b'r' { + should_not_reach(); + } + let result = (result as usize + 1) as *const c_char; + if *result != 0 { + should_not_reach(); + } + let result = getenv(b"not found\0" as *const _); + if result as usize != 0 { + should_not_reach(); + } +} +"#, + ); +} + #[test] fn posix_tls() { check_pass( diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 718df8331e2..51cf882d053 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -15,7 +15,7 @@ use hir_def::{ path::Path, resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs}, AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId, - TraitId, TypeOrConstParamId, + Lookup, TraitId, TypeOrConstParamId, }; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -372,7 +372,7 @@ impl<'ctx> MirLowerCtx<'ctx> { match &self.body.exprs[expr_id] { Expr::Missing => { if let DefWithBodyId::FunctionId(f) = self.owner { - let assoc = self.db.lookup_intern_function(f); + let assoc = f.lookup(self.db.upcast()); if let ItemContainerId::TraitId(t) = assoc.container { let name = &self.db.function_data(f).name; return Err(MirLowerError::TraitFunctionDefinition(t, name.clone())); @@ -1244,6 +1244,41 @@ impl<'ctx> MirLowerCtx<'ctx> { } } + fn lower_destructing_assignment( + &mut self, + mut current: BasicBlockId, + lhs: ExprId, + rhs: Place, + span: MirSpan, + ) -> Result> { + match &self.body.exprs[lhs] { + Expr::Tuple { exprs, is_assignee_expr: _ } => { + for (i, expr) in exprs.iter().enumerate() { + let Some(c) = self.lower_destructing_assignment( + current, + *expr, + rhs.project(ProjectionElem::TupleOrClosureField(i)), + span, + )? else { + return Ok(None); + }; + current = c; + } + Ok(Some(current)) + } + Expr::Underscore => Ok(Some(current)), + _ => { + let Some((lhs_place, current)) = + self.lower_expr_as_place(current, lhs, false)? + else { + return Ok(None); + }; + self.push_assignment(current, lhs_place, Operand::Copy(rhs).into(), span); + Ok(Some(current)) + } + } + } + fn lower_assignment( &mut self, current: BasicBlockId, @@ -1259,6 +1294,15 @@ impl<'ctx> MirLowerCtx<'ctx> { if matches!(&self.body.exprs[lhs], Expr::Underscore) { return Ok(Some(current)); } + if matches!( + &self.body.exprs[lhs], + Expr::Tuple { .. } | Expr::RecordLit { .. } | Expr::Call { .. } + ) { + let temp = self.temp(self.expr_ty_after_adjustments(rhs), current, rhs.into())?; + let temp = Place::from(temp); + self.push_assignment(current, temp.clone(), rhs_op.into(), span); + return self.lower_destructing_assignment(current, lhs, temp, span); + } let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)? else { @@ -1308,14 +1352,14 @@ impl<'ctx> MirLowerCtx<'ctx> { .resolve_path_in_value_ns(self.db.upcast(), c) .ok_or_else(unresolved_name)?; match pr { - ResolveValueResult::ValueNs(v) => { + ResolveValueResult::ValueNs(v, _) => { if let ValueNs::ConstId(c) = v { self.lower_const_to_operand(Substitution::empty(Interner), c.into(), ty) } else { not_supported!("bad path in range pattern"); } } - ResolveValueResult::Partial(_, _) => { + ResolveValueResult::Partial(_, _, _) => { not_supported!("associated constants in range pattern") } } diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 3354cbd76a0..1cdfd919742 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -323,7 +323,7 @@ impl MirLowerCtx<'_> { break 'b (c, x.1); } } - if let ResolveValueResult::ValueNs(v) = pr { + if let ResolveValueResult::ValueNs(v, _) = pr { if let ValueNs::ConstId(c) = v { break 'b (c, Substitution::empty(Interner)); } diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index 0f2fb2c8118..121e5a0a249 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -3,18 +3,19 @@ use hir_def::{ attr::{AttrsWithOwner, Documentation}, item_scope::ItemInNs, - path::ModPath, - resolver::HasResolver, - AttrDefId, GenericParamId, ModuleDefId, + path::{ModPath, Path}, + per_ns::Namespace, + resolver::{HasResolver, Resolver, TypeNs}, + AssocItemId, AttrDefId, GenericParamId, ModuleDefId, }; -use hir_expand::hygiene::Hygiene; +use hir_expand::{hygiene::Hygiene, name::Name}; use hir_ty::db::HirDatabase; use syntax::{ast, AstNode}; use crate::{ - Adt, AssocItem, Const, ConstParam, Enum, ExternCrateDecl, Field, Function, GenericParam, Impl, - LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait, TraitAlias, TypeAlias, - TypeParam, Union, Variant, + Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, Enum, ExternCrateDecl, Field, + Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait, + TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef, }; pub trait HasAttrs { @@ -25,14 +26,14 @@ pub trait HasAttrs { db: &dyn HirDatabase, link: &str, ns: Option, - ) -> Option; + ) -> Option; } -#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] -pub enum Namespace { - Types, - Values, - Macros, +/// Subset of `ide_db::Definition` that doc links can resolve to. +pub enum DocLinkDef { + ModuleDef(ModuleDef), + Field(Field), + SelfType(Trait), } macro_rules! impl_has_attrs { @@ -46,9 +47,14 @@ macro_rules! impl_has_attrs { let def = AttrDefId::$def_id(self.into()); db.attrs(def).docs() } - fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option) -> Option { + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option + ) -> Option { let def = AttrDefId::$def_id(self.into()); - resolve_doc_path(db, def, link, ns).map(ModuleDef::from) + resolve_doc_path(db, def, link, ns) } } )*}; @@ -79,7 +85,12 @@ macro_rules! impl_has_attrs_enum { fn docs(self, db: &dyn HirDatabase) -> Option { $enum::$variant(self).docs(db) } - fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option) -> Option { + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option + ) -> Option { $enum::$variant(self).resolve_doc_path(db, link, ns) } } @@ -111,7 +122,7 @@ impl HasAttrs for AssocItem { db: &dyn HirDatabase, link: &str, ns: Option, - ) -> Option { + ) -> Option { match self { AssocItem::Function(it) => it.resolve_doc_path(db, link, ns), AssocItem::Const(it) => it.resolve_doc_path(db, link, ns), @@ -147,9 +158,9 @@ impl HasAttrs for ExternCrateDecl { db: &dyn HirDatabase, link: &str, ns: Option, - ) -> Option { + ) -> Option { let def = AttrDefId::ExternCrateId(self.into()); - resolve_doc_path(db, def, link, ns).map(ModuleDef::from) + resolve_doc_path(db, def, link, ns) } } @@ -159,7 +170,7 @@ fn resolve_doc_path( def: AttrDefId, link: &str, ns: Option, -) -> Option { +) -> Option { let resolver = match def { AttrDefId::ModuleId(it) => it.resolver(db.upcast()), AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()), @@ -184,8 +195,107 @@ fn resolve_doc_path( .resolver(db.upcast()), }; - let modpath = { - // FIXME: this is not how we should get a mod path here + let mut modpath = modpath_from_str(db, link)?; + + let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath); + if resolved.is_none() { + let last_name = modpath.pop_segment()?; + resolve_assoc_or_field(db, resolver, modpath, last_name, ns) + } else { + let def = match ns { + Some(Namespace::Types) => resolved.take_types(), + Some(Namespace::Values) => resolved.take_values(), + Some(Namespace::Macros) => resolved.take_macros().map(ModuleDefId::MacroId), + None => resolved.iter_items().next().map(|(it, _)| match it { + ItemInNs::Types(it) => it, + ItemInNs::Values(it) => it, + ItemInNs::Macros(it) => ModuleDefId::MacroId(it), + }), + }; + Some(DocLinkDef::ModuleDef(def?.into())) + } +} + +fn resolve_assoc_or_field( + db: &dyn HirDatabase, + resolver: Resolver, + path: ModPath, + name: Name, + ns: Option, +) -> Option { + let path = Path::from_known_path_with_no_generic(path); + // FIXME: This does not handle `Self` on trait definitions, which we should resolve to the + // trait itself. + let base_def = resolver.resolve_path_in_type_ns_fully(db.upcast(), &path)?; + + let ty = match base_def { + TypeNs::SelfType(id) => Impl::from(id).self_ty(db), + TypeNs::GenericParam(_) => { + // Even if this generic parameter has some trait bounds, rustdoc doesn't + // resolve `name` to trait items. + return None; + } + TypeNs::AdtId(id) | TypeNs::AdtSelfType(id) => Adt::from(id).ty(db), + TypeNs::EnumVariantId(id) => { + // Enum variants don't have path candidates. + let variant = Variant::from(id); + return resolve_field(db, variant.into(), name, ns); + } + TypeNs::TypeAliasId(id) => { + let alias = TypeAlias::from(id); + if alias.as_assoc_item(db).is_some() { + // We don't normalize associated type aliases, so we have nothing to + // resolve `name` to. + return None; + } + alias.ty(db) + } + TypeNs::BuiltinType(id) => BuiltinType::from(id).ty(db), + TypeNs::TraitId(id) => { + // Doc paths in this context may only resolve to an item of this trait + // (i.e. no items of its supertraits), so we need to handle them here + // independently of others. + return db.trait_data(id).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| { + let def = match *assoc_id { + AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()), + AssocItemId::ConstId(it) => ModuleDef::Const(it.into()), + AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), + }; + DocLinkDef::ModuleDef(def) + }); + } + TypeNs::TraitAliasId(_) => { + // XXX: Do these get resolved? + return None; + } + }; + + // FIXME: Resolve associated items here, e.g. `Option::map`. Note that associated items take + // precedence over fields. + + let variant_def = match ty.as_adt()? { + Adt::Struct(it) => it.into(), + Adt::Union(it) => it.into(), + Adt::Enum(_) => return None, + }; + resolve_field(db, variant_def, name, ns) +} + +fn resolve_field( + db: &dyn HirDatabase, + def: VariantDef, + name: Name, + ns: Option, +) -> Option { + if let Some(Namespace::Types | Namespace::Macros) = ns { + return None; + } + def.fields(db).into_iter().find(|f| f.name(db) == name).map(DocLinkDef::Field) +} + +fn modpath_from_str(db: &dyn HirDatabase, link: &str) -> Option { + // FIXME: this is not how we should get a mod path here. + let try_get_modpath = |link: &str| { let ast_path = ast::SourceFile::parse(&format!("type T = {link};")) .syntax_node() .descendants() @@ -193,23 +303,20 @@ fn resolve_doc_path( if ast_path.syntax().text() != link { return None; } - ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())? + ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic()) }; - let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath); - let resolved = if resolved.is_none() { - resolver.resolve_module_path_in_trait_assoc_items(db.upcast(), &modpath)? - } else { - resolved - }; - match ns { - Some(Namespace::Types) => resolved.take_types(), - Some(Namespace::Values) => resolved.take_values(), - Some(Namespace::Macros) => resolved.take_macros().map(ModuleDefId::MacroId), - None => resolved.iter_items().next().map(|it| match it { - ItemInNs::Types(it) => it, - ItemInNs::Values(it) => it, - ItemInNs::Macros(it) => ModuleDefId::MacroId(it), - }), + let full = try_get_modpath(link); + if full.is_some() { + return full; } + + // Tuple field names cannot be a part of `ModPath` usually, but rustdoc can + // resolve doc paths like `TupleStruct::0`. + // FIXME: Find a better way to handle these. + let (base, maybe_tuple_field) = link.rsplit_once("::")?; + let tuple_field = Name::new_tuple_field(maybe_tuple_field.parse().ok()?); + let mut modpath = try_get_modpath(base)?; + modpath.push_segment(tuple_field); + Some(modpath) } diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 9dfb98e459b..ac171026d5d 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -8,7 +8,6 @@ use hir_def::{ type_ref::{TypeBound, TypeRef}, AdtId, GenericDefId, }; -use hir_expand::name; use hir_ty::{ display::{ write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, @@ -19,8 +18,9 @@ use hir_ty::{ use crate::{ Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl, Field, - Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, Static, Struct, - Trait, TraitAlias, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant, + Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, SelfParam, + Static, Struct, Trait, TraitAlias, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, + Union, Variant, }; impl HirDisplay for Function { @@ -57,37 +57,21 @@ impl HirDisplay for Function { f.write_char('(')?; - let write_self_param = |ty: &TypeRef, f: &mut HirFormatter<'_>| match ty { - TypeRef::Path(p) if p.is_self_type() => f.write_str("self"), - TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) => - { - f.write_char('&')?; - if let Some(lifetime) = lifetime { - write!(f, "{} ", lifetime.name.display(f.db.upcast()))?; - } - if let hir_def::type_ref::Mutability::Mut = mut_ { - f.write_str("mut ")?; - } - f.write_str("self") - } - _ => { - f.write_str("self: ")?; - ty.hir_fmt(f) - } - }; - let mut first = true; + let mut skip_self = 0; + if let Some(self_param) = self.self_param(db) { + self_param.hir_fmt(f)?; + first = false; + skip_self = 1; + } + // FIXME: Use resolved `param.ty` once we no longer discard lifetimes - for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)) { + for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) { let local = param.as_local(db).map(|it| it.name(db)); if !first { f.write_str(", ")?; } else { first = false; - if local == Some(name!(self)) { - write_self_param(type_ref, f)?; - continue; - } } match local { Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?, @@ -137,6 +121,31 @@ impl HirDisplay for Function { } } +impl HirDisplay for SelfParam { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + let data = f.db.function_data(self.func); + let param = data.params.first().unwrap(); + match &**param { + TypeRef::Path(p) if p.is_self_type() => f.write_str("self"), + TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) => + { + f.write_char('&')?; + if let Some(lifetime) = lifetime { + write!(f, "{} ", lifetime.name.display(f.db.upcast()))?; + } + if let hir_def::type_ref::Mutability::Mut = mut_ { + f.write_str("mut ")?; + } + f.write_str("self") + } + ty => { + f.write_str("self: ")?; + ty.hir_fmt(f) + } + } + } +} + impl HirDisplay for Adt { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self { @@ -357,6 +366,11 @@ fn write_generic_params( delim(f)?; write!(f, "const {}: ", name.display(f.db.upcast()))?; c.ty.hir_fmt(f)?; + + if let Some(default) = &c.default { + f.write_str(" = ")?; + write!(f, "{}", default.display(f.db.upcast()))?; + } } } } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index bf041b61f2f..512fe7e0428 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -63,12 +63,13 @@ use hir_ty::{ all_super_traits, autoderef, consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, + known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, primitive::UintTy, traits::FnTrait, - AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, + AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg, GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId, WhereClause, @@ -87,7 +88,7 @@ use triomphe::Arc; use crate::db::{DefDatabase, HirDatabase}; pub use crate::{ - attrs::{HasAttrs, Namespace}, + attrs::{DocLinkDef, HasAttrs}, diagnostics::{ AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode, IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, @@ -121,6 +122,7 @@ pub use { lang_item::LangItem, nameres::{DefMap, ModuleSource}, path::{ModPath, PathKind}, + per_ns::Namespace, type_ref::{Mutability, TypeRef}, visibility::Visibility, // FIXME: This is here since some queries take it as input that are used @@ -719,20 +721,18 @@ fn emit_def_diagnostic_( ) { match diag { DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => { - let decl = declaration.to_node(db.upcast()); + let decl = declaration.to_ptr(db.upcast()); acc.push( UnresolvedModule { - decl: InFile::new(declaration.file_id, AstPtr::new(&decl)), + decl: InFile::new(declaration.file_id, decl), candidates: candidates.clone(), } .into(), ) } DefDiagnosticKind::UnresolvedExternCrate { ast } => { - let item = ast.to_node(db.upcast()); - acc.push( - UnresolvedExternCrate { decl: InFile::new(ast.file_id, AstPtr::new(&item)) }.into(), - ); + let item = ast.to_ptr(db.upcast()); + acc.push(UnresolvedExternCrate { decl: InFile::new(ast.file_id, item) }.into()); } DefDiagnosticKind::UnresolvedImport { id, index } => { @@ -747,14 +747,10 @@ fn emit_def_diagnostic_( } DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { - let item = ast.to_node(db.upcast()); + let item = ast.to_ptr(db.upcast()); acc.push( - InactiveCode { - node: ast.with_value(SyntaxNodePtr::new(&item).into()), - cfg: cfg.clone(), - opts: opts.clone(), - } - .into(), + InactiveCode { node: ast.with_value(item), cfg: cfg.clone(), opts: opts.clone() } + .into(), ); } DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => { @@ -1273,7 +1269,7 @@ impl Adt { .fill(|x| { let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); match x { - ParamKind::Type => GenericArgData::Ty(r).intern(Interner), + ParamKind::Type => r.cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), } }) @@ -2096,14 +2092,6 @@ impl SelfParam { .unwrap_or(Access::Owned) } - pub fn display(self, db: &dyn HirDatabase) -> &'static str { - match self.access(db) { - Access::Shared => "&self", - Access::Exclusive => "&mut self", - Access::Owned => "self", - } - } - pub fn source(&self, db: &dyn HirDatabase) -> Option> { let InFile { file_id, value } = Function::from(self.func).source(db)?; value @@ -3142,12 +3130,8 @@ impl TypeParam { } pub fn default(self, db: &dyn HirDatabase) -> Option { - let params = db.generic_defaults(self.id.parent()); - let local_idx = hir_ty::param_idx(db, self.id.into())?; + let ty = generic_arg_from_param(db, self.id.into())?; let resolver = self.id.parent().resolver(db.upcast()); - let ty = params.get(local_idx)?.clone(); - let subst = TyBuilder::placeholder_subst(db, self.id.parent()); - let ty = ty.substitute(Interner, &subst); match ty.data(Interner) { GenericArgData::Ty(it) => { Some(Type::new_with_resolver_inner(db, &resolver, it.clone())) @@ -3209,6 +3193,19 @@ impl ConstParam { pub fn ty(self, db: &dyn HirDatabase) -> Type { Type::new(db, self.id.parent(), db.const_param_ty(self.id)) } + + pub fn default(self, db: &dyn HirDatabase) -> Option { + let arg = generic_arg_from_param(db, self.id.into())?; + known_const_to_ast(arg.constant(Interner)?, db) + } +} + +fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option { + let params = db.generic_defaults(id.parent); + let local_idx = hir_ty::param_idx(db, id)?; + let ty = params.get(local_idx)?.clone(); + let subst = TyBuilder::placeholder_subst(db, id.parent); + Some(ty.substitute(Interner, &subst)) } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -3716,7 +3713,7 @@ impl Type { .fill(|x| { let r = it.next().unwrap(); match x { - ParamKind::Type => GenericArgData::Ty(r).intern(Interner), + ParamKind::Type => r.cast(Interner), ParamKind::Const(ty) => { // FIXME: this code is not covered in tests. unknown_const_as_generic(ty.clone()) @@ -3749,9 +3746,7 @@ impl Type { .fill(|it| { // FIXME: this code is not covered in tests. match it { - ParamKind::Type => { - GenericArgData::Ty(args.next().unwrap().ty.clone()).intern(Interner) - } + ParamKind::Type => args.next().unwrap().ty.clone().cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), } }) @@ -4414,14 +4409,13 @@ impl Callable { Other => CallableKind::Other, } } - pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(ast::SelfParam, Type)> { + pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(SelfParam, Type)> { let func = match self.callee { Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it, _ => return None, }; - let src = func.lookup(db.upcast()).source(db.upcast()); - let param_list = src.value.param_list()?; - Some((param_list.self_param()?, self.ty.derived(self.sig.params()[0].clone()))) + let func = Function { id: func }; + Some((func.self_param(db)?, self.ty.derived(self.sig.params()[0].clone()))) } pub fn n_params(&self) -> usize { self.sig.params().len() - if self.is_bound_method { 1 } else { 0 } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index e99d2984c36..b8d4ecd4414 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -170,6 +170,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.is_derive_annotated(item) } + /// Expand the macro call with a different token tree, mapping the `token_to_map` down into the + /// expansion. `token_to_map` should be a token from the `speculative args` node. pub fn speculative_expand( &self, actual_macro_call: &ast::MacroCall, @@ -179,6 +181,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.speculative_expand(actual_macro_call, speculative_args, token_to_map) } + /// Expand the macro call with a different item as the input, mapping the `token_to_map` down into the + /// expansion. `token_to_map` should be a token from the `speculative args` node. pub fn speculative_expand_attr_macro( &self, actual_macro_call: &ast::Item, @@ -201,14 +205,22 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { ) } - /// Descend the token into macrocalls to its first mapped counterpart. - pub fn descend_into_macros_single(&self, token: SyntaxToken) -> SyntaxToken { - self.imp.descend_into_macros_single(token) + /// Descend the token into its macro call if it is part of one, returning the token in the + /// expansion that it is associated with. If `offset` points into the token's range, it will + /// be considered for the mapping in case of inline format args. + pub fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken { + self.imp.descend_into_macros_single(token, offset) } - /// Descend the token into macrocalls to all its mapped counterparts. - pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> { - self.imp.descend_into_macros(token) + /// Descend the token into its macro call if it is part of one, returning the tokens in the + /// expansion that it is associated with. If `offset` points into the token's range, it will + /// be considered for the mapping in case of inline format args. + pub fn descend_into_macros( + &self, + token: SyntaxToken, + offset: TextSize, + ) -> SmallVec<[SyntaxToken; 1]> { + self.imp.descend_into_macros(token, offset) } /// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token. @@ -217,12 +229,17 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn descend_into_macros_with_same_text( &self, token: SyntaxToken, + offset: TextSize, ) -> SmallVec<[SyntaxToken; 1]> { - self.imp.descend_into_macros_with_same_text(token) + self.imp.descend_into_macros_with_same_text(token, offset) } - pub fn descend_into_macros_with_kind_preference(&self, token: SyntaxToken) -> SyntaxToken { - self.imp.descend_into_macros_with_kind_preference(token) + pub fn descend_into_macros_with_kind_preference( + &self, + token: SyntaxToken, + offset: TextSize, + ) -> SyntaxToken { + self.imp.descend_into_macros_with_kind_preference(token, offset) } /// Maps a node down by mapping its first and last token down. @@ -606,7 +623,7 @@ impl<'db> SemanticsImpl<'db> { let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| { resolver .resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang)) - .map(|it| macro_id_to_def_id(self.db.upcast(), it)) + .map(|(it, _)| macro_id_to_def_id(self.db.upcast(), it)) })?; hir_expand::db::expand_speculative( self.db.upcast(), @@ -665,7 +682,7 @@ impl<'db> SemanticsImpl<'db> { }; if first == last { - self.descend_into_macros_impl(first, &mut |InFile { value, .. }| { + self.descend_into_macros_impl(first, 0.into(), &mut |InFile { value, .. }| { if let Some(node) = value.parent_ancestors().find_map(N::cast) { res.push(node) } @@ -674,7 +691,7 @@ impl<'db> SemanticsImpl<'db> { } else { // Descend first and last token, then zip them to look for the node they belong to let mut scratch: SmallVec<[_; 1]> = smallvec![]; - self.descend_into_macros_impl(first, &mut |token| { + self.descend_into_macros_impl(first, 0.into(), &mut |token| { scratch.push(token); false }); @@ -682,6 +699,7 @@ impl<'db> SemanticsImpl<'db> { let mut scratch = scratch.into_iter(); self.descend_into_macros_impl( last, + 0.into(), &mut |InFile { value: last, file_id: last_fid }| { if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() { if first_fid == last_fid { @@ -705,19 +723,27 @@ impl<'db> SemanticsImpl<'db> { res } - fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> { + fn descend_into_macros( + &self, + token: SyntaxToken, + offset: TextSize, + ) -> SmallVec<[SyntaxToken; 1]> { let mut res = smallvec![]; - self.descend_into_macros_impl(token, &mut |InFile { value, .. }| { + self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| { res.push(value); false }); res } - fn descend_into_macros_with_same_text(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> { + fn descend_into_macros_with_same_text( + &self, + token: SyntaxToken, + offset: TextSize, + ) -> SmallVec<[SyntaxToken; 1]> { let text = token.text(); let mut res = smallvec![]; - self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| { + self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| { if value.text() == text { res.push(value); } @@ -729,7 +755,11 @@ impl<'db> SemanticsImpl<'db> { res } - fn descend_into_macros_with_kind_preference(&self, token: SyntaxToken) -> SyntaxToken { + fn descend_into_macros_with_kind_preference( + &self, + token: SyntaxToken, + offset: TextSize, + ) -> SyntaxToken { let fetch_kind = |token: &SyntaxToken| match token.parent() { Some(node) => match node.kind() { kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => { @@ -741,7 +771,7 @@ impl<'db> SemanticsImpl<'db> { }; let preferred_kind = fetch_kind(&token); let mut res = None; - self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| { + self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| { if fetch_kind(&value) == preferred_kind { res = Some(value); true @@ -755,9 +785,9 @@ impl<'db> SemanticsImpl<'db> { res.unwrap_or(token) } - fn descend_into_macros_single(&self, token: SyntaxToken) -> SyntaxToken { + fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken { let mut res = token.clone(); - self.descend_into_macros_impl(token, &mut |InFile { value, .. }| { + self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| { res = value; true }); @@ -767,9 +797,13 @@ impl<'db> SemanticsImpl<'db> { fn descend_into_macros_impl( &self, token: SyntaxToken, + // FIXME: We might want this to be Option to be able to opt out of subrange + // mapping, specifically for node downmapping + offset: TextSize, f: &mut dyn FnMut(InFile) -> bool, ) { let _p = profile::span("descend_into_macros"); + let relative_token_offset = token.text_range().start().checked_sub(offset); let parent = match token.parent() { Some(it) => it, None => return, @@ -796,7 +830,12 @@ impl<'db> SemanticsImpl<'db> { self.cache(value, file_id); } - let mapped_tokens = expansion_info.map_token_down(self.db.upcast(), item, token)?; + let mapped_tokens = expansion_info.map_token_down( + self.db.upcast(), + item, + token, + relative_token_offset, + )?; let len = stack.len(); // requeue the tokens we got from mapping our current token down @@ -943,7 +982,7 @@ impl<'db> SemanticsImpl<'db> { offset: TextSize, ) -> impl Iterator + '_> + '_ { node.token_at_offset(offset) - .map(move |token| self.descend_into_macros(token)) + .map(move |token| self.descend_into_macros(token, offset)) .map(|descendants| { descendants.into_iter().map(move |it| self.token_ancestors_with_macros(it)) }) @@ -1683,6 +1722,14 @@ impl SemanticsScope<'_> { |name, id| cb(name, id.into()), ) } + + pub fn extern_crates(&self) -> impl Iterator + '_ { + self.resolver.extern_crates_in_scope().map(|(name, id)| (name, Module { id })) + } + + pub fn extern_crate_decls(&self) -> impl Iterator + '_ { + self.resolver.extern_crate_decls_in_scope(self.db.upcast()) + } } #[derive(Debug)] diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 3499daf1140..f29fb1edf00 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -487,7 +487,7 @@ impl SourceAnalyzer { let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?; self.resolver .resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Bang)) - .map(|it| it.into()) + .map(|(it, _)| it.into()) } pub(crate) fn resolve_bind_pat_to_const( @@ -760,7 +760,7 @@ impl SourceAnalyzer { let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| { self.resolver .resolve_path_as_macro(db.upcast(), &path, Some(MacroSubNs::Bang)) - .map(|it| macro_id_to_def_id(db.upcast(), it)) + .map(|(it, _)| macro_id_to_def_id(db.upcast(), it)) })?; Some(macro_call_id.as_file()).filter(|it| it.expansion_level(db.upcast()) < 64) } @@ -966,6 +966,7 @@ pub(crate) fn resolve_hir_path_as_attr_macro( ) -> Option { resolver .resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Attr)) + .map(|(it, _)| it) .map(Into::into) } @@ -983,7 +984,7 @@ fn resolve_hir_path_( res.map(|ty_ns| (ty_ns, path.segments().first())) } None => { - let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path)?; + let (ty, remaining_idx, _) = resolver.resolve_path_in_type_ns(db.upcast(), path)?; match remaining_idx { Some(remaining_idx) => { if remaining_idx + 1 == path.segments().len() { @@ -1067,7 +1068,7 @@ fn resolve_hir_path_( let macros = || { resolver .resolve_path_as_macro(db.upcast(), path.mod_path()?, None) - .map(|def| PathResolution::Def(ModuleDef::Macro(def.into()))) + .map(|(def, _)| PathResolution::Def(ModuleDef::Macro(def.into()))) }; if prefer_value_ns { values().or_else(types) } else { types().or_else(values) } diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index 43d957412bc..ca7874c3683 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -2,8 +2,10 @@ use base_db::FileRange; use hir_def::{ - src::HasSource, AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, - ModuleDefId, ModuleId, TraitId, + item_scope::ItemInNs, + src::{HasChildSource, HasSource}, + AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, ModuleDefId, ModuleId, + TraitId, }; use hir_expand::{HirFileId, InFile}; use hir_ty::db::HirDatabase; @@ -167,6 +169,40 @@ impl<'a> SymbolCollector<'a> { self.collect_from_impl(impl_id); } + // Record renamed imports. + // In case it imports multiple items under different namespaces we just pick one arbitrarily + // for now. + for id in scope.imports() { + let loc = id.import.lookup(self.db.upcast()); + loc.id.item_tree(self.db.upcast()); + let source = id.import.child_source(self.db.upcast()); + let Some(use_tree_src) = source.value.get(id.idx) else { continue }; + let Some(rename) = use_tree_src.rename() else { continue }; + let Some(name) = rename.name() else { continue }; + + let res = scope.fully_resolve_import(self.db.upcast(), id); + res.iter_items().for_each(|(item, _)| { + let def = match item { + ItemInNs::Types(def) | ItemInNs::Values(def) => def, + ItemInNs::Macros(def) => ModuleDefId::from(def), + } + .into(); + let dec_loc = DeclarationLocation { + hir_file_id: source.file_id, + ptr: SyntaxNodePtr::new(use_tree_src.syntax()), + name_ptr: SyntaxNodePtr::new(name.syntax()), + }; + + self.symbols.push(FileSymbol { + name: name.text().into(), + def, + container_name: self.current_container_name.clone(), + loc: dec_loc, + is_alias: false, + }); + }); + } + for const_id in scope.unnamed_consts() { self.collect_from_body(const_id); } diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 6aca716bb60..c0e5429a22c 100644 --- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -422,7 +422,7 @@ impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () { check_assist( add_missing_default_members, r#" -struct Bar { +struct Bar { bar: [i32, N] } @@ -439,7 +439,7 @@ impl Foo for S { $0 }"#, r#" -struct Bar { +struct Bar { bar: [i32, N] } @@ -483,6 +483,107 @@ impl Foo<42, {20 + 22}, X> for () { ) } + #[test] + fn test_const_substitution_with_defaults() { + check_assist( + add_missing_default_members, + r#" +trait Foo { + fn get_n(&self) -> usize { N } + fn get_m(&self) -> bool { M } + fn get_p(&self) -> char { P } + fn get_array(&self, arg: &T) -> [bool; N] { [M; N] } +} + +impl Foo for () { + $0 +}"#, + r#" +trait Foo { + fn get_n(&self) -> usize { N } + fn get_m(&self) -> bool { M } + fn get_p(&self) -> char { P } + fn get_array(&self, arg: &T) -> [bool; N] { [M; N] } +} + +impl Foo for () { + $0fn get_n(&self) -> usize { 42 } + + fn get_m(&self) -> bool { false } + + fn get_p(&self) -> char { 'a' } + + fn get_array(&self, arg: &X) -> [bool; 42] { [false; 42] } +}"#, + ); + } + + #[test] + fn test_const_substitution_with_defaults_2() { + check_assist( + add_missing_impl_members, + r#" +mod m { + pub const LEN: usize = 42; + pub trait Foo { + fn get_t(&self) -> T; + } +} + +impl m::Foo for () { + $0 +}"#, + r#" +mod m { + pub const LEN: usize = 42; + pub trait Foo { + fn get_t(&self) -> T; + } +} + +impl m::Foo for () { + fn get_t(&self) -> [bool; m::LEN] { + ${0:todo!()} + } +}"#, + ) + } + + #[test] + fn test_const_substitution_with_defaults_3() { + check_assist( + add_missing_default_members, + r#" +mod m { + pub const VAL: usize = 0; + + pub trait Foo { + fn get_n(&self) -> usize { N } + fn get_m(&self) -> usize { M } + } +} + +impl m::Foo for () { + $0 +}"#, + r#" +mod m { + pub const VAL: usize = 0; + + pub trait Foo { + fn get_n(&self) -> usize { N } + fn get_m(&self) -> usize { M } + } +} + +impl m::Foo for () { + $0fn get_n(&self) -> usize { {40 + 2} } + + fn get_m(&self) -> usize { {m::VAL + 1} } +}"#, + ) + } + #[test] fn test_cursor_after_empty_impl_def() { check_assist( diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs index 57cfa17cc8e..66bc2f6dadc 100644 --- a/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -1,6 +1,10 @@ use std::collections::VecDeque; -use syntax::ast::{self, AstNode}; +use syntax::{ + ast::{self, AstNode, Expr::BinExpr}, + ted::{self, Position}, + SyntaxKind, +}; use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; @@ -23,121 +27,117 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin // } // ``` pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let expr = ctx.find_node_at_offset::()?; - let op = expr.op_kind()?; - let op_range = expr.op_token()?.text_range(); + let mut bin_expr = ctx.find_node_at_offset::()?; + let op = bin_expr.op_kind()?; + let op_range = bin_expr.op_token()?.text_range(); - let opposite_op = match op { - ast::BinaryOp::LogicOp(ast::LogicOp::And) => "||", - ast::BinaryOp::LogicOp(ast::LogicOp::Or) => "&&", - _ => return None, - }; - - let cursor_in_range = op_range.contains_range(ctx.selection_trimmed()); - if !cursor_in_range { + // Is the cursor on the expression's logical operator? + if !op_range.contains_range(ctx.selection_trimmed()) { return None; } - let mut expr = expr; - // Walk up the tree while we have the same binary operator - while let Some(parent_expr) = expr.syntax().parent().and_then(ast::BinExpr::cast) { - match expr.op_kind() { + while let Some(parent_expr) = bin_expr.syntax().parent().and_then(ast::BinExpr::cast) { + match parent_expr.op_kind() { Some(parent_op) if parent_op == op => { - expr = parent_expr; + bin_expr = parent_expr; } _ => break, } } - let mut expr_stack = vec![expr.clone()]; - let mut terms = Vec::new(); - let mut op_ranges = Vec::new(); + let op = bin_expr.op_kind()?; + let inv_token = match op { + ast::BinaryOp::LogicOp(ast::LogicOp::And) => SyntaxKind::PIPE2, + ast::BinaryOp::LogicOp(ast::LogicOp::Or) => SyntaxKind::AMP2, + _ => return None, + }; - // Find all the children with the same binary operator - while let Some(expr) = expr_stack.pop() { - let mut traverse_bin_expr_arm = |expr| { - if let ast::Expr::BinExpr(bin_expr) = expr { - if let Some(expr_op) = bin_expr.op_kind() { - if expr_op == op { - expr_stack.push(bin_expr); - } else { - terms.push(ast::Expr::BinExpr(bin_expr)); - } + let demorganed = bin_expr.clone_subtree().clone_for_update(); + + ted::replace(demorganed.op_token()?, ast::make::token(inv_token)); + let mut exprs = VecDeque::from(vec![ + (bin_expr.lhs()?, demorganed.lhs()?), + (bin_expr.rhs()?, demorganed.rhs()?), + ]); + + while let Some((expr, dm)) = exprs.pop_front() { + if let BinExpr(bin_expr) = &expr { + if let BinExpr(cbin_expr) = &dm { + if op == bin_expr.op_kind()? { + ted::replace(cbin_expr.op_token()?, ast::make::token(inv_token)); + exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?)); + exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?)); } else { - terms.push(ast::Expr::BinExpr(bin_expr)); + let mut inv = invert_boolean_expression(expr); + if inv.needs_parens_in(dm.syntax().parent()?) { + inv = ast::make::expr_paren(inv).clone_for_update(); + } + ted::replace(dm.syntax(), inv.syntax()); } } else { - terms.push(expr); + return None; } - }; - - op_ranges.extend(expr.op_token().map(|t| t.text_range())); - traverse_bin_expr_arm(expr.lhs()?); - traverse_bin_expr_arm(expr.rhs()?); + } else { + let mut inv = invert_boolean_expression(dm.clone_subtree()).clone_for_update(); + if inv.needs_parens_in(dm.syntax().parent()?) { + inv = ast::make::expr_paren(inv).clone_for_update(); + } + ted::replace(dm.syntax(), inv.syntax()); + } } + let dm_lhs = demorganed.lhs()?; + acc.add( AssistId("apply_demorgan", AssistKind::RefactorRewrite), "Apply De Morgan's law", op_range, |edit| { - terms.sort_by_key(|t| t.syntax().text_range().start()); - let mut terms = VecDeque::from(terms); - - let paren_expr = expr.syntax().parent().and_then(ast::ParenExpr::cast); - + let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast); let neg_expr = paren_expr .clone() .and_then(|paren_expr| paren_expr.syntax().parent()) .and_then(ast::PrefixExpr::cast) .and_then(|prefix_expr| { - if prefix_expr.op_kind().unwrap() == ast::UnaryOp::Not { + if prefix_expr.op_kind()? == ast::UnaryOp::Not { Some(prefix_expr) } else { None } }); - for op_range in op_ranges { - edit.replace(op_range, opposite_op); - } - if let Some(paren_expr) = paren_expr { - for term in terms { - let range = term.syntax().text_range(); - let not_term = invert_boolean_expression(term); - - edit.replace(range, not_term.syntax().text()); - } - if let Some(neg_expr) = neg_expr { cov_mark::hit!(demorgan_double_negation); - edit.replace(neg_expr.op_token().unwrap().text_range(), ""); + edit.replace_ast(ast::Expr::PrefixExpr(neg_expr), demorganed.into()); } else { cov_mark::hit!(demorgan_double_parens); - edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!("); + ted::insert_all_raw( + Position::before(dm_lhs.syntax()), + vec![ + syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)), + syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)), + ], + ); + + ted::append_child_raw( + demorganed.syntax(), + syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::R_PAREN)), + ); + + edit.replace_ast(ast::Expr::ParenExpr(paren_expr), demorganed.into()); } } else { - if let Some(lhs) = terms.pop_front() { - let lhs_range = lhs.syntax().text_range(); - let not_lhs = invert_boolean_expression(lhs); - - edit.replace(lhs_range, format!("!({not_lhs}")); - } - - if let Some(rhs) = terms.pop_back() { - let rhs_range = rhs.syntax().text_range(); - let not_rhs = invert_boolean_expression(rhs); - - edit.replace(rhs_range, format!("{not_rhs})")); - } - - for term in terms { - let term_range = term.syntax().text_range(); - let not_term = invert_boolean_expression(term); - edit.replace(term_range, not_term.to_string()); - } + ted::insert_all_raw( + Position::before(dm_lhs.syntax()), + vec![ + syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)), + syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)), + ], + ); + ted::append_child_raw(demorganed.syntax(), ast::make::token(SyntaxKind::R_PAREN)); + edit.replace_ast(bin_expr, demorganed); } }, ) @@ -145,9 +145,8 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti #[cfg(test)] mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; - use super::*; + use crate::tests::{check_assist, check_assist_not_applicable}; #[test] fn demorgan_handles_leq() { @@ -213,7 +212,7 @@ fn f() { !(S <= S || S < S) } #[test] fn demorgan_doesnt_double_negation() { cov_mark::check!(demorgan_double_negation); - check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }") + check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { !x && !x }") } #[test] @@ -222,13 +221,38 @@ fn f() { !(S <= S || S < S) } check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }") } - // https://github.com/rust-lang/rust-analyzer/issues/10963 + // FIXME : This needs to go. + // // https://github.com/rust-lang/rust-analyzer/issues/10963 + // #[test] + // fn demorgan_doesnt_hang() { + // check_assist( + // apply_demorgan, + // "fn f() { 1 || 3 &&$0 4 || 5 }", + // "fn f() { !(!1 || !3 || !4) || 5 }", + // ) + // } + #[test] - fn demorgan_doesnt_hang() { + fn demorgan_keep_pars_for_op_precedence() { check_assist( apply_demorgan, - "fn f() { 1 || 3 &&$0 4 || 5 }", - "fn f() { !(!1 || !3 || !4) || 5 }", + "fn main() { + let _ = !(!a ||$0 !(b || c)); +} +", + "fn main() { + let _ = a && (b || c); +} +", + ); + } + + #[test] + fn demorgan_removes_pars_in_eq_precedence() { + check_assist( + apply_demorgan, + "fn() { let x = a && !(!b |$0| !c); }", + "fn() { let x = a && b && c; }", ) } } diff --git a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index fe1cb6fce36..76f021ed912 100644 --- a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -161,9 +161,9 @@ fn process_struct_name_reference( let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?; // A `PathSegment` always belongs to a `Path`, so there's at least one `Path` at this point. let full_path = - path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last().unwrap(); + path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last()?; - if full_path.segment().unwrap().name_ref()? != *name_ref { + if full_path.segment()?.name_ref()? != *name_ref { // `name_ref` isn't the last segment of the path, so `full_path` doesn't point to the // struct we want to edit. return None; diff --git a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index dcb96ab8af4..7d0e424769e 100644 --- a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -58,7 +58,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<' return None; } - let bound_ident = pat.fields().next().unwrap(); + let bound_ident = pat.fields().next()?; if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) { return None; } @@ -108,6 +108,15 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<' then_block.syntax().last_child_or_token().filter(|t| t.kind() == T!['}'])?; + let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update(); + + let end_of_then = then_block_items.syntax().last_child_or_token()?; + let end_of_then = if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { + end_of_then.prev_sibling_or_token()? + } else { + end_of_then + }; + let target = if_expr.syntax().text_range(); acc.add( AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite), @@ -141,16 +150,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<' } }; - let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update(); - - let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); - let end_of_then = - if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { - end_of_then.prev_sibling_or_token().unwrap() - } else { - end_of_then - }; - let then_statements = replacement .children_with_tokens() .chain( diff --git a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index 4f3b6e0c287..c3d925cb26c 100644 --- a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -48,7 +48,7 @@ pub(crate) fn extract_expressions_from_format_string( let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?; let expanded_t = ast::String::cast( - ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone()), + ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone(), 0.into()), )?; if !is_format_string(&expanded_t) { return None; diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index b8b781ea48d..ea7a21e77a4 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -750,7 +750,7 @@ impl FunctionBody { .descendants_with_tokens() .filter_map(SyntaxElement::into_token) .filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self])) - .flat_map(|t| sema.descend_into_macros(t)) + .flat_map(|t| sema.descend_into_macros(t, 0.into())) .for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast))); } } @@ -810,7 +810,7 @@ impl FunctionBody { (true, konst.body(), Some(sema.to_def(&konst)?.ty(sema.db))) }, ast::ConstParam(cp) => { - (true, cp.default_val(), Some(sema.to_def(&cp)?.ty(sema.db))) + (true, cp.default_val()?.expr(), Some(sema.to_def(&cp)?.ty(sema.db))) }, ast::ConstBlockPat(cbp) => { let expr = cbp.block_expr().map(ast::Expr::BlockExpr); @@ -1385,31 +1385,30 @@ enum FlowHandler { impl FlowHandler { fn from_ret_ty(fun: &Function, ret_ty: &FunType) -> FlowHandler { - match &fun.control_flow.kind { - None => FlowHandler::None, - Some(flow_kind) => { - let action = flow_kind.clone(); - if let FunType::Unit = ret_ty { - match flow_kind { - FlowKind::Return(None) - | FlowKind::Break(_, None) - | FlowKind::Continue(_) => FlowHandler::If { action }, - FlowKind::Return(_) | FlowKind::Break(_, _) => { - FlowHandler::IfOption { action } - } - FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() }, - } - } else { - match flow_kind { - FlowKind::Return(None) - | FlowKind::Break(_, None) - | FlowKind::Continue(_) => FlowHandler::MatchOption { none: action }, - FlowKind::Return(_) | FlowKind::Break(_, _) => { - FlowHandler::MatchResult { err: action } - } - FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() }, - } + if fun.contains_tail_expr { + return FlowHandler::None; + } + let Some(action) = fun.control_flow.kind.clone() else { + return FlowHandler::None; + }; + + if let FunType::Unit = ret_ty { + match action { + FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => { + FlowHandler::If { action } } + FlowKind::Return(_) | FlowKind::Break(_, _) => FlowHandler::IfOption { action }, + FlowKind::Try { kind } => FlowHandler::Try { kind }, + } + } else { + match action { + FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => { + FlowHandler::MatchOption { none: action } + } + FlowKind::Return(_) | FlowKind::Break(_, _) => { + FlowHandler::MatchResult { err: action } + } + FlowKind::Try { kind } => FlowHandler::Try { kind }, } } } @@ -1654,11 +1653,7 @@ impl Function { fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option { let fun_ty = self.return_type(ctx); - let handler = if self.contains_tail_expr { - FlowHandler::None - } else { - FlowHandler::from_ret_ty(self, &fun_ty) - }; + let handler = FlowHandler::from_ret_ty(self, &fun_ty); let ret_ty = match &handler { FlowHandler::None => { if matches!(fun_ty, FunType::Unit) { @@ -1728,11 +1723,7 @@ fn make_body( fun: &Function, ) -> ast::BlockExpr { let ret_ty = fun.return_type(ctx); - let handler = if fun.contains_tail_expr { - FlowHandler::None - } else { - FlowHandler::from_ret_ty(fun, &ret_ty) - }; + let handler = FlowHandler::from_ret_ty(fun, &ret_ty); let block = match &fun.body { FunctionBody::Expr(expr) => { @@ -4471,7 +4462,7 @@ async fn foo() -> Result<(), ()> { "#, r#" async fn foo() -> Result<(), ()> { - fun_name().await? + fun_name().await } async fn $0fun_name() -> Result<(), ()> { @@ -4690,7 +4681,7 @@ fn $0fun_name() { check_assist( extract_function, r#" -//- minicore: result +//- minicore: result, try fn foo() -> Result<(), i64> { $0Result::::Ok(0)?; Ok(())$0 @@ -4698,7 +4689,7 @@ fn foo() -> Result<(), i64> { "#, r#" fn foo() -> Result<(), i64> { - fun_name()? + fun_name() } fn $0fun_name() -> Result<(), i64> { @@ -5753,6 +5744,34 @@ fn $0fun_name(t: T, v: V) -> i32 where T: Into + Copy, V: Into { ); } + #[test] + fn tail_expr_no_extra_control_flow() { + check_assist( + extract_function, + r#" +//- minicore: result +fn fallible() -> Result<(), ()> { + $0if true { + return Err(()); + } + Ok(())$0 +} +"#, + r#" +fn fallible() -> Result<(), ()> { + fun_name() +} + +fn $0fun_name() -> Result<(), ()> { + if true { + return Err(()); + } + Ok(()) +} +"#, + ); + } + #[test] fn non_tail_expr_of_tail_expr_loop() { check_assist( @@ -5800,12 +5819,6 @@ fn $0fun_name() -> ControlFlow<()> { extract_function, r#" //- minicore: option, try -impl core::ops::Try for Option { - type Output = T; - type Residual = Option; -} -impl core::ops::FromResidual for Option {} - fn f() -> Option<()> { if true { let a = $0if true { @@ -5820,12 +5833,6 @@ fn f() -> Option<()> { } "#, r#" -impl core::ops::Try for Option { - type Output = T; - type Residual = Option; -} -impl core::ops::FromResidual for Option {} - fn f() -> Option<()> { if true { let a = fun_name()?;; @@ -5852,12 +5859,6 @@ fn $0fun_name() -> Option<()> { extract_function, r#" //- minicore: option, try -impl core::ops::Try for Option { - type Output = T; - type Residual = Option; -} -impl core::ops::FromResidual for Option {} - fn f() -> Option<()> { if true { $0{ @@ -5874,15 +5875,9 @@ fn f() -> Option<()> { } "#, r#" -impl core::ops::Try for Option { - type Output = T; - type Residual = Option; -} -impl core::ops::FromResidual for Option {} - fn f() -> Option<()> { if true { - fun_name()? + fun_name() } else { None } diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 31fc69562c9..bbac0a26ea4 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -95,6 +95,9 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else { continue; }; + + let field = make::ext::field_from_idents(["self", &field_name])?; + acc.add_group( &GroupLabel("Generate delegate methods…".to_owned()), AssistId("generate_delegate_methods", AssistKind::Generate), @@ -115,11 +118,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' Some(list) => convert_param_list_to_arg_list(list), None => make::arg_list([]), }; - let tail_expr = make::expr_method_call( - make::ext::field_from_idents(["self", &field_name]).unwrap(), // This unwrap is ok because we have at least 1 arg in the list - make::name_ref(&name), - arg_list, - ); + let tail_expr = make::expr_method_call(field, make::name_ref(&name), arg_list); let ret_type = method_source.ret_type(); let is_async = method_source.async_token().is_some(); let is_const = method_source.const_token().is_some(); diff --git a/crates/ide-assists/src/handlers/generate_derive.rs b/crates/ide-assists/src/handlers/generate_derive.rs index 747f70f9f6f..53ba144ba9e 100644 --- a/crates/ide-assists/src/handlers/generate_derive.rs +++ b/crates/ide-assists/src/handlers/generate_derive.rs @@ -27,13 +27,19 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt let cap = ctx.config.snippet_cap?; let nominal = ctx.find_node_at_offset::()?; let target = nominal.syntax().text_range(); + let derive_attr = nominal + .attrs() + .filter_map(|x| x.as_simple_call()) + .filter(|(name, _arg)| name == "derive") + .map(|(_name, arg)| arg) + .next(); + + let delimiter = match &derive_attr { + None => None, + Some(tt) => Some(tt.right_delimiter_token()?), + }; + acc.add(AssistId("generate_derive", AssistKind::Generate), "Add `#[derive]`", target, |edit| { - let derive_attr = nominal - .attrs() - .filter_map(|x| x.as_simple_call()) - .filter(|(name, _arg)| name == "derive") - .map(|(_name, arg)| arg) - .next(); match derive_attr { None => { let derive = make::attr_outer(make::meta_token_tree( @@ -45,16 +51,23 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt let nominal = edit.make_mut(nominal); nominal.add_attr(derive.clone()); + let delimiter = derive + .meta() + .expect("make::attr_outer was expected to have Meta") + .token_tree() + .expect("failed to get token tree out of Meta") + .r_paren_token() + .expect("make::attr_outer was expected to have a R_PAREN"); + + edit.add_tabstop_before_token(cap, delimiter); + } + Some(_) => { + // Just move the cursor. edit.add_tabstop_before_token( cap, - derive.meta().unwrap().token_tree().unwrap().r_paren_token().unwrap(), + delimiter.expect("Right delim token could not be found."), ); } - Some(tt) => { - // Just move the cursor. - let tt = edit.make_mut(tt); - edit.add_tabstop_before_token(cap, tt.right_delimiter_token().unwrap()); - } }; }) } diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs index a403d5bc672..e2b82223289 100644 --- a/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/crates/ide-assists/src/handlers/remove_dbg.rs @@ -39,14 +39,11 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let replacements = macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::>(); - if replacements.is_empty() { - return None; - } acc.add( AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", - replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range)).unwrap(), + replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range))?, |builder| { for (range, expr) in replacements { if let Some(expr) = expr { diff --git a/crates/ide-assists/src/handlers/remove_unused_imports.rs b/crates/ide-assists/src/handlers/remove_unused_imports.rs index dd4839351fb..5fcab8c02b0 100644 --- a/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/crates/ide-assists/src/handlers/remove_unused_imports.rs @@ -67,7 +67,7 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) // This case maps to the situation where the * token is braced. // In this case, the parent use tree's path is the one we should use to resolve the glob. match u.syntax().ancestors().skip(1).find_map(ast::UseTree::cast) { - Some(parent_u) if parent_u.path().is_some() => parent_u.path().unwrap(), + Some(parent_u) if parent_u.path().is_some() => parent_u.path()?, _ => return None, } } else { diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 480cb77b4fd..f60ac150164 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -20,6 +20,7 @@ pub(crate) mod r#type; pub(crate) mod use_; pub(crate) mod vis; pub(crate) mod env_vars; +pub(crate) mod extern_crate; use std::iter; @@ -703,7 +704,9 @@ pub(super) fn complete_name_ref( TypeLocation::TypeAscription(ascription) => { r#type::complete_ascribed_type(acc, ctx, path_ctx, ascription); } - TypeLocation::GenericArgList(_) + TypeLocation::GenericArg { .. } + | TypeLocation::AssocConstEq + | TypeLocation::AssocTypeEq | TypeLocation::TypeBound | TypeLocation::ImplTarget | TypeLocation::ImplTrait @@ -737,6 +740,7 @@ pub(super) fn complete_name_ref( } } } + NameRefKind::ExternCrate => extern_crate::complete_extern_crate(acc, ctx), NameRefKind::DotAccess(dot_access) => { flyimport::import_on_the_fly_dot(acc, ctx, dot_access); dot::complete_dot(acc, ctx, dot_access); diff --git a/crates/ide-completion/src/completions/extern_crate.rs b/crates/ide-completion/src/completions/extern_crate.rs new file mode 100644 index 00000000000..0d0e143f5f6 --- /dev/null +++ b/crates/ide-completion/src/completions/extern_crate.rs @@ -0,0 +1,71 @@ +//! Completion for extern crates + +use hir::{HasAttrs, Name}; +use ide_db::SymbolKind; + +use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; + +use super::Completions; + +pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionContext<'_>) { + let imported_extern_crates: Vec = ctx.scope.extern_crate_decls().collect(); + + for (name, module) in ctx.scope.extern_crates() { + if imported_extern_crates.contains(&name) { + continue; + } + + let mut item = CompletionItem::new( + CompletionItemKind::SymbolKind(SymbolKind::Module), + ctx.source_range(), + name.to_smol_str(), + ); + item.set_documentation(module.docs(ctx.db)); + + item.add_to(acc, ctx.db); + } +} + +#[cfg(test)] +mod test { + use crate::tests::completion_list_no_kw; + + #[test] + fn can_complete_extern_crate() { + let case = r#" +//- /lib.rs crate:other_crate_a +// nothing here +//- /other_crate_b.rs crate:other_crate_b +pub mod good_mod{} +//- /lib.rs crate:crate_c +// nothing here +//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a +extern crate oth$0 +mod other_mod {} +"#; + + let completion_list = completion_list_no_kw(case); + + assert_eq!("md other_crate_a\n".to_string(), completion_list); + } + + #[test] + fn will_not_complete_existing_import() { + let case = r#" +//- /lib.rs crate:other_crate_a +// nothing here +//- /lib.rs crate:crate_c +// nothing here +//- /lib.rs crate:other_crate_b +// +//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a,other_crate_b +extern crate other_crate_b; +extern crate oth$0 +mod other_mod {} +"#; + + let completion_list = completion_list_no_kw(case); + + assert_eq!("md other_crate_a\n".to_string(), completion_list); + } +} diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index e4705475638..a30fd13b1d5 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -1,7 +1,7 @@ //! Completion of names from the current scope in type position. use hir::{HirDisplay, ScopeDef}; -use syntax::{ast, AstNode, SyntaxKind}; +use syntax::{ast, AstNode}; use crate::{ context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation}, @@ -20,16 +20,15 @@ pub(crate) fn complete_type_path( let scope_def_applicable = |def| { use hir::{GenericParam::*, ModuleDef::*}; match def { - ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false, + ScopeDef::GenericParam(LifetimeParam(_)) => location.complete_lifetimes(), + ScopeDef::Label(_) => false, // no values in type places ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false, // unless its a constant in a generic arg list position ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => { - matches!(location, TypeLocation::GenericArgList(_)) - } - ScopeDef::ImplSelfType(_) => { - !matches!(location, TypeLocation::ImplTarget | TypeLocation::ImplTrait) + location.complete_consts() } + ScopeDef::ImplSelfType(_) => location.complete_self_type(), // Don't suggest attribute macros and derives. ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db), // Type things are fine @@ -38,12 +37,12 @@ pub(crate) fn complete_type_path( ) | ScopeDef::AdtSelfType(_) | ScopeDef::Unknown - | ScopeDef::GenericParam(TypeParam(_)) => true, + | ScopeDef::GenericParam(TypeParam(_)) => location.complete_types(), } }; let add_assoc_item = |acc: &mut Completions, item| match item { - hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArgList(_)) => { + hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArg { .. }) => { acc.add_const(ctx, ct) } hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (), @@ -157,56 +156,30 @@ pub(crate) fn complete_type_path( }); return; } - TypeLocation::GenericArgList(Some(arg_list)) => { - let in_assoc_type_arg = ctx - .original_token - .parent_ancestors() - .any(|node| node.kind() == SyntaxKind::ASSOC_TYPE_ARG); + TypeLocation::GenericArg { + args: Some(arg_list), of_trait: Some(trait_), .. + } => { + if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() { + let arg_idx = arg_list + .generic_args() + .filter(|arg| { + arg.syntax().text_range().end() + < ctx.original_token.text_range().start() + }) + .count(); - if !in_assoc_type_arg { - if let Some(path_seg) = - arg_list.syntax().parent().and_then(ast::PathSegment::cast) - { - if path_seg - .syntax() - .ancestors() - .find_map(ast::TypeBound::cast) - .is_some() - { - if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait( - trait_, - ))) = ctx.sema.resolve_path(&path_seg.parent_path()) - { - let arg_idx = arg_list - .generic_args() - .filter(|arg| { - arg.syntax().text_range().end() - < ctx.original_token.text_range().start() - }) - .count(); - - let n_required_params = - trait_.type_or_const_param_count(ctx.sema.db, true); - if arg_idx >= n_required_params { - trait_ - .items_with_supertraits(ctx.sema.db) - .into_iter() - .for_each(|it| { - if let hir::AssocItem::TypeAlias(alias) = it { - cov_mark::hit!( - complete_assoc_type_in_generics_list - ); - acc.add_type_alias_with_eq(ctx, alias); - } - }); - - let n_params = - trait_.type_or_const_param_count(ctx.sema.db, false); - if arg_idx >= n_params { - return; // only show assoc types - } - } + let n_required_params = trait_.type_or_const_param_count(ctx.sema.db, true); + if arg_idx >= n_required_params { + trait_.items_with_supertraits(ctx.sema.db).into_iter().for_each(|it| { + if let hir::AssocItem::TypeAlias(alias) = it { + cov_mark::hit!(complete_assoc_type_in_generics_list); + acc.add_type_alias_with_eq(ctx, alias); } + }); + + let n_params = trait_.type_or_const_param_count(ctx.sema.db, false); + if arg_idx >= n_params { + return; // only show assoc types } } } diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 3cb65b2729a..0da7ba6d000 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -155,13 +155,63 @@ pub(crate) struct ExprCtx { pub(crate) enum TypeLocation { TupleField, TypeAscription(TypeAscriptionTarget), - GenericArgList(Option), + /// Generic argument position e.g. `Foo<$0>` + GenericArg { + /// The generic argument list containing the generic arg + args: Option, + /// `Some(trait_)` if `trait_` is being instantiated with `args` + of_trait: Option, + /// The generic parameter being filled in by the generic arg + corresponding_param: Option, + }, + /// Associated type equality constraint e.g. `Foo` + AssocTypeEq, + /// Associated constant equality constraint e.g. `Foo` + AssocConstEq, TypeBound, ImplTarget, ImplTrait, Other, } +impl TypeLocation { + pub(crate) fn complete_lifetimes(&self) -> bool { + matches!( + self, + TypeLocation::GenericArg { + corresponding_param: Some(ast::GenericParam::LifetimeParam(_)), + .. + } + ) + } + + pub(crate) fn complete_consts(&self) -> bool { + match self { + TypeLocation::GenericArg { + corresponding_param: Some(ast::GenericParam::ConstParam(_)), + .. + } => true, + TypeLocation::AssocConstEq => true, + _ => false, + } + } + + pub(crate) fn complete_types(&self) -> bool { + match self { + TypeLocation::GenericArg { corresponding_param: Some(param), .. } => { + matches!(param, ast::GenericParam::TypeParam(_)) + } + TypeLocation::AssocConstEq => false, + TypeLocation::AssocTypeEq => true, + _ => true, + } + } + + pub(crate) fn complete_self_type(&self) -> bool { + self.complete_types() && !matches!(self, TypeLocation::ImplTarget | TypeLocation::ImplTrait) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum TypeAscriptionTarget { Let(Option), @@ -301,6 +351,7 @@ pub(super) enum NameRefKind { expr: ast::RecordExpr, }, Pattern(PatternContext), + ExternCrate, } /// The identifier we are currently completing. diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 3ea50659030..1e6b2f319aa 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -1,11 +1,11 @@ //! Module responsible for analyzing the code surrounding the cursor for completion. use std::iter; -use hir::{Semantics, Type, TypeInfo, Variant}; +use hir::{HasSource, Semantics, Type, TypeInfo, Variant}; use ide_db::{active_parameter::ActiveParameter, RootDatabase}; use syntax::{ algo::{find_node_at_offset, non_trivia_sibling}, - ast::{self, AttrKind, HasArgList, HasLoopBody, HasName, NameOrNameRef}, + ast::{self, AttrKind, HasArgList, HasGenericParams, HasLoopBody, HasName, NameOrNameRef}, match_ast, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, T, }; @@ -624,6 +624,10 @@ fn classify_name_ref( }); return Some(make_res(kind)); }, + ast::ExternCrate(_) => { + let kind = NameRefKind::ExternCrate; + return Some(make_res(kind)); + }, ast::MethodCallExpr(method) => { let receiver = find_opt_node_in_file(original_file, method.receiver()); let kind = NameRefKind::DotAccess(DotAccess { @@ -719,6 +723,136 @@ fn classify_name_ref( None }; + let generic_arg_location = |arg: ast::GenericArg| { + let mut override_location = None; + let location = find_opt_node_in_file_compensated( + sema, + original_file, + arg.syntax().parent().and_then(ast::GenericArgList::cast), + ) + .map(|args| { + let mut in_trait = None; + let param = (|| { + let parent = args.syntax().parent()?; + let params = match_ast! { + match parent { + ast::PathSegment(segment) => { + match sema.resolve_path(&segment.parent_path().top_path())? { + hir::PathResolution::Def(def) => match def { + hir::ModuleDef::Function(func) => { + func.source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::Adt(adt) => { + adt.source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::Variant(variant) => { + variant.parent_enum(sema.db).source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::Trait(trait_) => { + if let ast::GenericArg::AssocTypeArg(arg) = &arg { + let arg_name = arg.name_ref()?; + let arg_name = arg_name.text(); + for item in trait_.items_with_supertraits(sema.db) { + match item { + hir::AssocItem::TypeAlias(assoc_ty) => { + if assoc_ty.name(sema.db).as_str()? == arg_name { + override_location = Some(TypeLocation::AssocTypeEq); + return None; + } + }, + hir::AssocItem::Const(const_) => { + if const_.name(sema.db)?.as_str()? == arg_name { + override_location = Some(TypeLocation::AssocConstEq); + return None; + } + }, + _ => (), + } + } + return None; + } else { + in_trait = Some(trait_); + trait_.source(sema.db)?.value.generic_param_list() + } + } + hir::ModuleDef::TraitAlias(trait_) => { + trait_.source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::TypeAlias(ty_) => { + ty_.source(sema.db)?.value.generic_param_list() + } + _ => None, + }, + _ => None, + } + }, + ast::MethodCallExpr(call) => { + let func = sema.resolve_method_call(&call)?; + func.source(sema.db)?.value.generic_param_list() + }, + ast::AssocTypeArg(arg) => { + let trait_ = ast::PathSegment::cast(arg.syntax().parent()?.parent()?)?; + match sema.resolve_path(&trait_.parent_path().top_path())? { + hir::PathResolution::Def(def) => match def { + hir::ModuleDef::Trait(trait_) => { + let arg_name = arg.name_ref()?; + let arg_name = arg_name.text(); + let trait_items = trait_.items_with_supertraits(sema.db); + let assoc_ty = trait_items.iter().find_map(|item| match item { + hir::AssocItem::TypeAlias(assoc_ty) => { + (assoc_ty.name(sema.db).as_str()? == arg_name) + .then_some(assoc_ty) + }, + _ => None, + })?; + assoc_ty.source(sema.db)?.value.generic_param_list() + } + _ => None, + }, + _ => None, + } + }, + _ => None, + } + }?; + // Determine the index of the argument in the `GenericArgList` and match it with + // the corresponding parameter in the `GenericParamList`. Since lifetime parameters + // are often omitted, ignore them for the purposes of matching the argument with + // its parameter unless a lifetime argument is provided explicitly. That is, for + // `struct S<'a, 'b, T>`, match `S::<$0>` to `T` and `S::<'a, $0, _>` to `'b`. + // FIXME: This operates on the syntax tree and will produce incorrect results when + // generic parameters are disabled by `#[cfg]` directives. It should operate on the + // HIR, but the functionality necessary to do so is not exposed at the moment. + let mut explicit_lifetime_arg = false; + let arg_idx = arg + .syntax() + .siblings(Direction::Prev) + // Skip the node itself + .skip(1) + .map(|arg| if ast::LifetimeArg::can_cast(arg.kind()) { explicit_lifetime_arg = true }) + .count(); + let param_idx = if explicit_lifetime_arg { + arg_idx + } else { + // Lifetimes parameters always precede type and generic parameters, + // so offset the argument index by the total number of lifetime params + arg_idx + params.lifetime_params().count() + }; + params.generic_params().nth(param_idx) + })(); + (args, in_trait, param) + }); + let (arg_list, of_trait, corresponding_param) = match location { + Some((arg_list, of_trait, param)) => (Some(arg_list), of_trait, param), + _ => (None, None, None), + }; + override_location.unwrap_or(TypeLocation::GenericArg { + args: arg_list, + of_trait, + corresponding_param, + }) + }; + let type_location = |node: &SyntaxNode| { let parent = node.parent()?; let res = match_ast! { @@ -774,9 +908,12 @@ fn classify_name_ref( ast::TypeBound(_) => TypeLocation::TypeBound, // is this case needed? ast::TypeBoundList(_) => TypeLocation::TypeBound, - ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))), + ast::GenericArg(it) => generic_arg_location(it), // is this case needed? - ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))), + ast::GenericArgList(it) => { + let args = find_opt_node_in_file_compensated(sema, original_file, Some(it)); + TypeLocation::GenericArg { args, of_trait: None, corresponding_param: None } + }, ast::TupleField(_) => TypeLocation::TupleField, _ => return None, } diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 8c038c0fbaa..4cdfd546f6a 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -1286,3 +1286,57 @@ macro_rules! println { expect![""], ) } + +#[test] +fn no_completions_for_external_doc_hidden_in_path() { + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + Span$0 +} +//- /lib.rs crate:dep +#[doc(hidden)] +pub mod bridge { + pub mod server { + pub trait Span + } +} +pub mod bridge2 { + #[doc(hidden)] + pub mod server2 { + pub trait Span + } +} +"#, + expect![""], + ); + // unless re-exported + check( + r#" +//- /main.rs crate:main deps:dep +fn main() { + Span$0 +} +//- /lib.rs crate:dep +#[doc(hidden)] +pub mod bridge { + pub mod server { + pub trait Span + } +} +pub use bridge::server::Span; +pub mod bridge2 { + #[doc(hidden)] + pub mod server2 { + pub trait Span2 + } +} +pub use bridge2::server2::Span2; +"#, + expect![[r#" + tt Span (use dep::Span) + tt Span2 (use dep::Span2) + "#]], + ); +} diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 8cb1ff4a125..d518dd76410 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -384,10 +384,8 @@ trait Trait2: Trait1 { fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} "#, expect![[r#" - ct CONST - cp CONST_PARAM en Enum - ma makro!(…) macro_rules! makro + ma makro!(…) macro_rules! makro md module st Record st Tuple @@ -404,14 +402,13 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} ); check( r#" -trait Trait2 { +trait Trait2 { type Foo; } fn foo<'lt, T: Trait2, const CONST_PARAM: usize>(_: T) {} "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -437,7 +434,6 @@ trait Tr { impl Tr<$0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -485,7 +481,6 @@ trait MyTrait { fn f(t: impl MyTrait { fn f(t: impl MyTrait { fn f(t: impl MyTrait { fn f(t: impl MyTrait() {} + fn main() { + foo::(); + } + "#, + expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], + ); + // FIXME: This should probably also suggest completions for types, at least those that have + // associated constants usable in this position. For example, a user could be typing + // `foo::<_, { usize::MAX }>()`, but we currently don't suggest `usize` in constant position. + check( + r#" + struct Foo; + const X: usize = 0; + fn foo() {} + fn main() { + foo::<_, $0>(); + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Method generic params + check( + r#" + const X: usize = 0; + struct Foo; + impl Foo { fn bar(self) {} } + fn main() { + Foo.bar::<_, $0>(); + } + "#, + expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], + ); + check( + r#" + const X: usize = 0; + struct Foo; + impl Foo { fn bar(self) {} } + fn main() { + Foo.bar::(); + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Associated type generic params + check( + r#" + const X: usize = 0; + struct Foo; + trait Bar { + type Baz; + } + fn foo(_: impl Bar = ()>) {} + "#, + expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Bar + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], + ); + check( + r#" + const X: usize = 0; + struct Foo; + trait Bar { + type Baz; + } + fn foo = ()>>() {} + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Type generic params + check( + r#" + const X: usize = 0; + struct Foo(T); + fn main() { + let _: Foo::<_, $0> = Foo(()); + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Type alias generic params + check( + r#" + const X: usize = 0; + struct Foo(T); + type Bar = Foo; + fn main() { + let _: Bar:: = Bar(()); + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Enum variant params + check( + r#" + const X: usize = 0; + enum Foo { A(T), B } + fn main() { + Foo::B::<(), $0>; + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Trait params + check( + r#" + const X: usize = 0; + trait Foo {} + impl Foo<(), $0> for () {} + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Trait alias params + check( + r#" + #![feature(trait_alias)] + const X: usize = 0; + trait Foo {} + trait Bar = Foo; + fn foo>() {} + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Omitted lifetime params + check( + r#" +struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); +fn foo<'a>() { S::; } + "#, + expect![[r#" + ct CONST + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + // Explicit lifetime params + check( + r#" +struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); +fn foo<'a>() { S::<'static, 'static, F$0, _>; } + "#, + expect![[r#" + ct CONST + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + check( + r#" +struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); +fn foo<'a>() { S::<'static, F$0, _, _>; } + "#, + expect![[r#" + lt 'a + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); +} diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index 5e4562d9c58..4ce80532e8e 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -7,7 +7,7 @@ use arrayvec::ArrayVec; use hir::{ - Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, + Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, DocLinkDef, ExternCrateDecl, Field, Function, GenericParam, HasVisibility, Impl, Label, Local, Macro, Module, ModuleDef, Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant, Visibility, @@ -649,3 +649,13 @@ impl From for Definition { } } } + +impl From for Definition { + fn from(def: DocLinkDef) -> Self { + match def { + DocLinkDef::ModuleDef(it) => it.into(), + DocLinkDef::Field(it) => it.into(), + DocLinkDef::SelfType(it) => it.into(), + } + } +} diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs index 1eb8f00020b..330af442f75 100644 --- a/crates/ide-db/src/helpers.rs +++ b/crates/ide-db/src/helpers.rs @@ -117,7 +117,7 @@ pub fn get_definition( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, ) -> Option { - for token in sema.descend_into_macros(token) { + for token in sema.descend_into_macros(token, 0.into()) { let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); if let Some(&[x]) = def.as_deref() { return Some(x); diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index e52dc356775..e475c5cd66b 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -6,7 +6,7 @@ use hir::{ use itertools::Itertools; use rustc_hash::FxHashSet; use syntax::{ - ast::{self, HasName}, + ast::{self, make, HasName}, utils::path_to_string_stripping_turbo_fish, AstNode, SyntaxNode, }; @@ -607,7 +607,7 @@ impl ImportCandidate { fn for_name(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option { if sema .scope(name.syntax())? - .speculative_resolve(&ast::make::ext::ident_path(&name.text())) + .speculative_resolve(&make::ext::ident_path(&name.text())) .is_some() { return None; diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index f27ed485d81..ac3511ba47b 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -94,18 +94,21 @@ impl fmt::Debug for RootDatabase { } impl Upcast for RootDatabase { + #[inline] fn upcast(&self) -> &(dyn ExpandDatabase + 'static) { &*self } } impl Upcast for RootDatabase { + #[inline] fn upcast(&self) -> &(dyn DefDatabase + 'static) { &*self } } impl Upcast for RootDatabase { + #[inline] fn upcast(&self) -> &(dyn HirDatabase + 'static) { &*self } diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index 1d0cb426a57..fb75b5b4584 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -5,7 +5,7 @@ use either::Either; use hir::{AsAssocItem, HirDisplay, SemanticsScope}; use rustc_hash::FxHashMap; use syntax::{ - ast::{self, AstNode}, + ast::{self, make, AstNode}, ted, SyntaxNode, }; @@ -21,6 +21,7 @@ enum TypeOrConst { } type LifetimeName = String; +type DefaultedParam = Either; /// `PathTransform` substitutes path in SyntaxNodes in bulk. /// @@ -115,7 +116,7 @@ impl<'a> PathTransform<'a> { }; let mut type_substs: FxHashMap = Default::default(); let mut const_substs: FxHashMap = Default::default(); - let mut default_types: Vec = Default::default(); + let mut defaulted_params: Vec = Default::default(); self.generic_def .into_iter() .flat_map(|it| it.type_params(db)) @@ -138,8 +139,8 @@ impl<'a> PathTransform<'a> { if let Some(default) = &default.display_source_code(db, source_module.into(), false).ok() { - type_substs.insert(k, ast::make::ty(default).clone_for_update()); - default_types.push(k); + type_substs.insert(k, make::ty(default).clone_for_update()); + defaulted_params.push(Either::Left(k)); } } } @@ -155,11 +156,19 @@ impl<'a> PathTransform<'a> { // is a standalone statement or a part of another expresson) // and sometimes require slight modifications; see // https://doc.rust-lang.org/reference/statements.html#expression-statements + // (default values in curly brackets can cause the same problem) const_substs.insert(k, expr.syntax().clone()); } } - (Either::Left(_), None) => (), // FIXME: get default const value - _ => (), // ignore mismatching params + (Either::Left(k), None) => { + if let Some(default) = k.default(db) { + if let Some(default) = default.expr() { + const_substs.insert(k, default.syntax().clone_for_update()); + defaulted_params.push(Either::Right(k)); + } + } + } + _ => (), // ignore mismatching params }); let lifetime_substs: FxHashMap<_, _> = self .generic_def @@ -175,7 +184,7 @@ impl<'a> PathTransform<'a> { target_module, source_scope: self.source_scope, }; - ctx.transform_default_type_substs(default_types); + ctx.transform_default_values(defaulted_params); ctx } } @@ -212,13 +221,19 @@ impl Ctx<'_> { }); } - fn transform_default_type_substs(&self, default_types: Vec) { - for k in default_types { - let v = self.type_substs.get(&k).unwrap(); + fn transform_default_values(&self, defaulted_params: Vec) { + // By now the default values are simply copied from where they are declared + // and should be transformed. As any value is allowed to refer to previous + // generic (both type and const) parameters, they should be all iterated left-to-right. + for param in defaulted_params { + let value = match param { + Either::Left(k) => self.type_substs.get(&k).unwrap().syntax(), + Either::Right(k) => self.const_substs.get(&k).unwrap(), + }; // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec // so that such operation is safe. - let paths = postorder(&v.syntax()).filter_map(ast::Path::cast).collect::>(); + let paths = postorder(value).filter_map(ast::Path::cast).collect::>(); for path in paths { self.transform_path(path); } @@ -263,15 +278,14 @@ impl Ctx<'_> { hir::ModuleDef::Trait(trait_ref), false, )?; - match ast::make::ty_path(mod_path_to_ast(&found_path)) { + match make::ty_path(mod_path_to_ast(&found_path)) { ast::Type::PathType(path_ty) => Some(path_ty), _ => None, } }); - let segment = ast::make::path_segment_ty(subst.clone(), trait_ref); - let qualified = - ast::make::path_from_segments(std::iter::once(segment), false); + let segment = make::path_segment_ty(subst.clone(), trait_ref); + let qualified = make::path_from_segments(std::iter::once(segment), false); ted::replace(path.syntax(), qualified.clone_for_update().syntax()); } else if let Some(path_ty) = ast::PathType::cast(parent) { ted::replace( diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index d5abd099126..7e00d368652 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -456,14 +456,14 @@ impl<'a> FindUsages<'a> { it.text().trim_start_matches("r#") == name }) .into_iter() - .flat_map(|token| { + .flat_map(move |token| { // FIXME: There should be optimization potential here // Currently we try to descend everything we find which // means we call `Semantics::descend_into_macros` on // every textual hit. That function is notoriously // expensive even for things that do not get down mapped // into macros. - sema.descend_into_macros(token).into_iter().filter_map(|it| it.parent()) + sema.descend_into_macros(token, offset).into_iter().filter_map(|it| it.parent()) }) }; diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs index b54c43b296b..f699f999baf 100644 --- a/crates/ide-db/src/symbol_index.rs +++ b/crates/ide-db/src/symbol_index.rs @@ -323,6 +323,8 @@ impl Query { hir::ModuleDef::Adt(..) | hir::ModuleDef::TypeAlias(..) | hir::ModuleDef::BuiltinType(..) + | hir::ModuleDef::TraitAlias(..) + | hir::ModuleDef::Trait(..) ) { continue; @@ -417,9 +419,16 @@ const CONST_WITH_INNER: () = { mod b_mod; + +use define_struct as really_define_struct; +use Macro as ItemLikeMacro; +use Macro as Trait; // overlay namespaces //- /b_mod.rs struct StructInModB; - "#, +use super::Macro as SuperItemLikeMacro; +use crate::b_mod::StructInModB as ThisStruct; +use crate::Trait as IsThisJustATrait; +"#, ); let symbols: Vec<_> = Crate::from(db.test_crate()) diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 1a00e29384e..87ad5844c64 100644 --- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -118,6 +118,35 @@ container_name: None, is_alias: false, }, + FileSymbol { + name: "ItemLikeMacro", + def: Macro( + Macro { + id: Macro2Id( + Macro2Id( + 0, + ), + ), + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 0, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 654..676, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 663..676, + }, + }, + container_name: None, + is_alias: false, + }, FileSymbol { name: "Macro", def: Macro( @@ -352,6 +381,35 @@ container_name: None, is_alias: false, }, + FileSymbol { + name: "Trait", + def: Macro( + Macro { + id: Macro2Id( + Macro2Id( + 0, + ), + ), + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 0, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 682..696, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 691..696, + }, + }, + container_name: None, + is_alias: false, + }, FileSymbol { name: "Union", def: Adt( @@ -551,6 +609,35 @@ container_name: None, is_alias: false, }, + FileSymbol { + name: "really_define_struct", + def: Macro( + Macro { + id: MacroRulesId( + MacroRulesId( + 1, + ), + ), + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 0, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 611..648, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 628..648, + }, + }, + container_name: None, + is_alias: false, + }, FileSymbol { name: "trait_fn", def: Function( @@ -631,6 +718,35 @@ }, }, [ + FileSymbol { + name: "IsThisJustATrait", + def: Macro( + Macro { + id: Macro2Id( + Macro2Id( + 0, + ), + ), + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 1, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 111..143, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 127..143, + }, + }, + container_name: None, + is_alias: false, + }, FileSymbol { name: "StructInModB", def: Adt( @@ -660,6 +776,93 @@ container_name: None, is_alias: false, }, + FileSymbol { + name: "SuperItemLikeMacro", + def: Macro( + Macro { + id: Macro2Id( + Macro2Id( + 0, + ), + ), + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 1, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 25..59, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 41..59, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { + name: "ThisStruct", + def: Adt( + Struct( + Struct { + id: StructId( + 3, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 1, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 65..105, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 95..105, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { + name: "ThisStruct", + def: Adt( + Struct( + Struct { + id: StructId( + 3, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: FileId( + FileId( + 1, + ), + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 65..105, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 95..105, + }, + }, + container_name: None, + is_alias: false, + }, ], ), ] diff --git a/crates/ide-db/src/use_trivial_constructor.rs b/crates/ide-db/src/use_trivial_constructor.rs index f96ea29ae2f..a915391ad90 100644 --- a/crates/ide-db/src/use_trivial_constructor.rs +++ b/crates/ide-db/src/use_trivial_constructor.rs @@ -1,31 +1,29 @@ //! Functionality for generating trivial constructors use hir::StructKind; -use syntax::ast; +use syntax::ast::{make, Expr, Path}; /// given a type return the trivial constructor (if one exists) pub fn use_trivial_constructor( db: &crate::RootDatabase, - path: ast::Path, + path: Path, ty: &hir::Type, -) -> Option { +) -> Option { match ty.as_adt() { Some(hir::Adt::Enum(x)) => { if let &[variant] = &*x.variants(db) { if variant.kind(db) == hir::StructKind::Unit { - let path = ast::make::path_qualified( + let path = make::path_qualified( path, - syntax::ast::make::path_segment(ast::make::name_ref( - &variant.name(db).to_smol_str(), - )), + make::path_segment(make::name_ref(&variant.name(db).to_smol_str())), ); - return Some(syntax::ast::make::expr_path(path)); + return Some(make::expr_path(path)); } } } Some(hir::Adt::Struct(x)) if x.kind(db) == StructKind::Unit => { - return Some(syntax::ast::make::expr_path(path)); + return Some(make::expr_path(path)); } _ => {} } diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs index a8e88369088..60fcbbbd397 100644 --- a/crates/ide-ssr/src/matching.rs +++ b/crates/ide-ssr/src/matching.rs @@ -560,8 +560,10 @@ impl<'db, 'sema> Matcher<'db, 'sema> { placeholder_value.autoref_kind = self .sema .resolve_method_call_as_callable(code) - .and_then(|callable| callable.receiver_param(self.sema.db)) - .map(|(self_param, _)| self_param.kind()) + .and_then(|callable| { + let (self_param, _) = callable.receiver_param(self.sema.db)?; + Some(self_param.source(self.sema.db)?.value.kind()) + }) .unwrap_or(ast::SelfParamKind::Owned); } } diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index dd1d0d75c63..f834f2ce592 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs @@ -74,18 +74,20 @@ pub(crate) fn incoming_calls( Some(calls.into_items()) } -pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option> { +pub(crate) fn outgoing_calls( + db: &RootDatabase, + FilePosition { file_id, offset }: FilePosition, +) -> Option> { let sema = Semantics::new(db); - let file_id = position.file_id; let file = sema.parse(file_id); let file = file.syntax(); - let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind { + let token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT => 1, _ => 0, })?; let mut calls = CallLocations::default(); - sema.descend_into_macros(token) + sema.descend_into_macros(token, offset) .into_iter() .filter_map(|it| it.parent_ancestors().nth(1).and_then(ast::Item::cast)) .filter_map(|item| match item { diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index d240127f376..901f7a2a79a 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -131,19 +131,19 @@ pub(crate) fn remove_links(markdown: &str) -> String { // |=== pub(crate) fn external_docs( db: &RootDatabase, - position: &FilePosition, + FilePosition { file_id, offset }: FilePosition, target_dir: Option<&OsStr>, sysroot: Option<&OsStr>, ) -> Option { let sema = &Semantics::new(db); - let file = sema.parse(position.file_id).syntax().clone(); - let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind { + let file = sema.parse(file_id).syntax().clone(); + let token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT | INT_NUMBER | T![self] => 3, T!['('] | T![')'] => 2, kind if kind.is_trivia() => 0, _ => 1, })?; - let token = sema.descend_into_macros_single(token); + let token = sema.descend_into_macros_single(token, offset); let node = token.parent()?; let definition = match_ast! { @@ -285,7 +285,7 @@ impl DocCommentToken { let original_start = doc_token.text_range().start(); let relative_comment_offset = offset - original_start - prefix_len; - sema.descend_into_macros(doc_token).into_iter().find_map(|t| { + sema.descend_into_macros(doc_token, offset).into_iter().find_map(|t| { let (node, descended_prefix_len) = match_ast! { match t { ast::Comment(comment) => (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?), diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs index 05a64b33bfd..8036c77072b 100644 --- a/crates/ide/src/doc_links/tests.rs +++ b/crates/ide/src/doc_links/tests.rs @@ -517,6 +517,62 @@ fn function(); ) } +#[test] +fn doc_links_field() { + check_doc_links( + r#" +/// [`S::f`] +/// [`S2::f`] +/// [`T::0`] +/// [`U::a`] +/// [`E::A::f`] +/// [`E::B::0`] +struct S$0 { + f: i32, + //^ S::f + //^ S2::f +} +type S2 = S; +struct T(i32); + //^^^ T::0 +union U { + a: i32, + //^ U::a +} +enum E { + A { f: i32 }, + //^ E::A::f + B(i32), + //^^^ E::B::0 +} +"#, + ); +} + +#[test] +fn doc_links_field_via_self() { + check_doc_links( + r#" +/// [`Self::f`] +struct S$0 { + f: i32, + //^ Self::f +} +"#, + ); +} + +#[test] +fn doc_links_tuple_field_via_self() { + check_doc_links( + r#" +/// [`Self::0`] +struct S$0(i32); + //^^^ Self::0 +"#, + ); +} + #[test] fn rewrite_html_root_url() { check_rewrite( diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index d6bbf2bf794..119a9c7c3f4 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -40,28 +40,33 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< // struct Bar; // ``` - let derive = sema.descend_into_macros(tok.clone()).into_iter().find_map(|descended| { - let hir_file = sema.hir_file_for(&descended.parent()?); - if !hir_file.is_derive_attr_pseudo_expansion(db) { - return None; - } + let derive = + sema.descend_into_macros(tok.clone(), 0.into()).into_iter().find_map(|descended| { + let hir_file = sema.hir_file_for(&descended.parent()?); + if !hir_file.is_derive_attr_pseudo_expansion(db) { + return None; + } - let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string(); - // up map out of the #[derive] expansion - let token = hir::InFile::new(hir_file, descended).upmap(db)?.value; - let attr = token.parent_ancestors().find_map(ast::Attr::cast)?; - let expansions = sema.expand_derive_macro(&attr)?; - let idx = attr - .token_tree()? - .token_trees_and_tokens() - .filter_map(NodeOrToken::into_token) - .take_while(|it| it != &token) - .filter(|it| it.kind() == T![,]) - .count(); - let expansion = - format(db, SyntaxKind::MACRO_ITEMS, position.file_id, expansions.get(idx).cloned()?); - Some(ExpandedMacro { name, expansion }) - }); + let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string(); + // up map out of the #[derive] expansion + let token = hir::InFile::new(hir_file, descended).upmap(db)?.value; + let attr = token.parent_ancestors().find_map(ast::Attr::cast)?; + let expansions = sema.expand_derive_macro(&attr)?; + let idx = attr + .token_tree()? + .token_trees_and_tokens() + .filter_map(NodeOrToken::into_token) + .take_while(|it| it != &token) + .filter(|it| it.kind() == T![,]) + .count(); + let expansion = format( + db, + SyntaxKind::MACRO_ITEMS, + position.file_id, + expansions.get(idx).cloned()?, + ); + Some(ExpandedMacro { name, expansion }) + }); if derive.is_some() { return derive; diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs index f9061822244..3d89599c583 100644 --- a/crates/ide/src/extend_selection.rs +++ b/crates/ide/src/extend_selection.rs @@ -17,8 +17,6 @@ use crate::FileRange; // Extends or shrinks the current selection to the encompassing syntactic construct // (expression, statement, item, module, etc). It works with multiple cursors. // -// This is a standard LSP feature and not a protocol extension. -// // |=== // | Editor | Shortcut // @@ -142,8 +140,10 @@ fn extend_tokens_from_range( // compute original mapped token range let extended = { - let fst_expanded = sema.descend_into_macros_single(first_token.clone()); - let lst_expanded = sema.descend_into_macros_single(last_token.clone()); + let fst_expanded = + sema.descend_into_macros_single(first_token.clone(), original_range.start()); + let lst_expanded = + sema.descend_into_macros_single(last_token.clone(), original_range.end()); let mut lca = algo::least_common_ancestor(&fst_expanded.parent()?, &lst_expanded.parent()?)?; lca = shallowest_node(&lca); @@ -154,13 +154,16 @@ fn extend_tokens_from_range( }; // Compute parent node range - let validate = |token: &SyntaxToken| -> bool { - let expanded = sema.descend_into_macros_single(token.clone()); - let parent = match expanded.parent() { - Some(it) => it, - None => return false, - }; - algo::least_common_ancestor(&extended, &parent).as_ref() == Some(&extended) + let validate = |offset: TextSize| { + let extended = &extended; + move |token: &SyntaxToken| -> bool { + let expanded = sema.descend_into_macros_single(token.clone(), offset); + let parent = match expanded.parent() { + Some(it) => it, + None => return false, + }; + algo::least_common_ancestor(extended, &parent).as_ref() == Some(extended) + } }; // Find the first and last text range under expanded parent @@ -168,14 +171,14 @@ fn extend_tokens_from_range( let token = token.prev_token()?; skip_trivia_token(token, Direction::Prev) }) - .take_while(validate) + .take_while(validate(original_range.start())) .last()?; let last = successors(Some(last_token), |token| { let token = token.next_token()?; skip_trivia_token(token, Direction::Next) }) - .take_while(validate) + .take_while(validate(original_range.end())) .last()?; let range = first.text_range().cover(last.text_range()); diff --git a/crates/ide/src/goto_declaration.rs b/crates/ide/src/goto_declaration.rs index c39c696cfd9..7e0fab42608 100644 --- a/crates/ide/src/goto_declaration.rs +++ b/crates/ide/src/goto_declaration.rs @@ -20,16 +20,16 @@ use crate::{ // - fields in patterns will navigate to the field declaration of the struct, union or variant pub(crate) fn goto_declaration( db: &RootDatabase, - position: FilePosition, + position @ FilePosition { file_id, offset }: FilePosition, ) -> Option>> { let sema = Semantics::new(db); - let file = sema.parse(position.file_id).syntax().clone(); + let file = sema.parse(file_id).syntax().clone(); let original_token = file - .token_at_offset(position.offset) + .token_at_offset(offset) .find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate] | T![Self]))?; let range = original_token.text_range(); let info: Vec = sema - .descend_into_macros(original_token) + .descend_into_macros(original_token, offset) .iter() .filter_map(|token| { let parent = token.parent()?; diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 21471ab2a03..e09b9f39148 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -29,45 +29,39 @@ use syntax::{ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T}; // image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[] pub(crate) fn goto_definition( db: &RootDatabase, - position: FilePosition, + FilePosition { file_id, offset }: FilePosition, ) -> Option>> { let sema = &Semantics::new(db); - let file = sema.parse(position.file_id).syntax().clone(); - let original_token = - pick_best_token(file.token_at_offset(position.offset), |kind| match kind { - IDENT - | INT_NUMBER - | LIFETIME_IDENT - | T![self] - | T![super] - | T![crate] - | T![Self] - | COMMENT => 4, - // index and prefix ops - T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3, - kind if kind.is_keyword() => 2, - T!['('] | T![')'] => 2, - kind if kind.is_trivia() => 0, - _ => 1, - })?; + let file = sema.parse(file_id).syntax().clone(); + let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { + IDENT + | INT_NUMBER + | LIFETIME_IDENT + | T![self] + | T![super] + | T![crate] + | T![Self] + | COMMENT => 4, + // index and prefix ops + T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3, + kind if kind.is_keyword() => 2, + T!['('] | T![')'] => 2, + kind if kind.is_trivia() => 0, + _ => 1, + })?; if let Some(doc_comment) = token_as_doc_comment(&original_token) { - return doc_comment.get_definition_with_descend_at( - sema, - position.offset, - |def, _, link_range| { - let nav = def.try_to_nav(db)?; - Some(RangeInfo::new(link_range, vec![nav])) - }, - ); + return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, link_range| { + let nav = def.try_to_nav(db)?; + Some(RangeInfo::new(link_range, vec![nav])) + }); } let navs = sema - .descend_into_macros(original_token.clone()) + .descend_into_macros(original_token.clone(), offset) .into_iter() .filter_map(|token| { let parent = token.parent()?; if let Some(tt) = ast::TokenTree::cast(parent) { - if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), position.file_id) - { + if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), file_id) { return Some(vec![x]); } } diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 37166bdbd0c..544c6b42317 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs @@ -22,20 +22,19 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; // image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[] pub(crate) fn goto_implementation( db: &RootDatabase, - position: FilePosition, + FilePosition { file_id, offset }: FilePosition, ) -> Option>> { let sema = Semantics::new(db); - let source_file = sema.parse(position.file_id); + let source_file = sema.parse(file_id); let syntax = source_file.syntax().clone(); - let original_token = - pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind { - IDENT | T![self] | INT_NUMBER => 1, - _ => 0, - })?; + let original_token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind { + IDENT | T![self] | INT_NUMBER => 1, + _ => 0, + })?; let range = original_token.text_range(); let navs = - sema.descend_into_macros(original_token) + sema.descend_into_macros(original_token, offset) .into_iter() .filter_map(|token| token.parent().and_then(ast::NameLike::cast)) .filter_map(|node| match &node { diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index 6048990f749..955923d7691 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs @@ -16,13 +16,13 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; // image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[] pub(crate) fn goto_type_definition( db: &RootDatabase, - position: FilePosition, + FilePosition { file_id, offset }: FilePosition, ) -> Option>> { let sema = hir::Semantics::new(db); - let file: ast::SourceFile = sema.parse(position.file_id); + let file: ast::SourceFile = sema.parse(file_id); let token: SyntaxToken = - pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { + pick_best_token(file.syntax().token_at_offset(offset), |kind| match kind { IDENT | INT_NUMBER | T![self] => 2, kind if kind.is_trivia() => 0, _ => 1, @@ -37,7 +37,7 @@ pub(crate) fn goto_type_definition( } }; let range = token.text_range(); - sema.descend_into_macros(token) + sema.descend_into_macros(token, offset) .into_iter() .filter_map(|token| { let ty = sema diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index 43e89a334bf..46a0464e9e6 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -15,6 +15,7 @@ use syntax::{ SyntaxKind::{self, IDENT, INT_NUMBER}, SyntaxNode, SyntaxToken, TextRange, T, }; +use text_edit::TextSize; use crate::{navigation_target::ToNav, references, NavigationTarget, TryToNav}; @@ -51,7 +52,7 @@ pub struct HighlightRelatedConfig { pub(crate) fn highlight_related( sema: &Semantics<'_, RootDatabase>, config: HighlightRelatedConfig, - FilePosition { offset, file_id }: FilePosition, + pos @ FilePosition { offset, file_id }: FilePosition, ) -> Option> { let _p = profile::span("highlight_related"); let syntax = sema.parse(file_id).syntax().clone(); @@ -79,7 +80,7 @@ pub(crate) fn highlight_related( } T![|] if config.closure_captures => highlight_closure_captures(sema, token, file_id), T![move] if config.closure_captures => highlight_closure_captures(sema, token, file_id), - _ if config.references => highlight_references(sema, &syntax, token, file_id), + _ if config.references => highlight_references(sema, &syntax, token, pos), _ => None, } } @@ -129,9 +130,9 @@ fn highlight_references( sema: &Semantics<'_, RootDatabase>, node: &SyntaxNode, token: SyntaxToken, - file_id: FileId, + FilePosition { file_id, offset }: FilePosition, ) -> Option> { - let defs = find_defs(sema, token.clone()); + let defs = find_defs(sema, token.clone(), offset); let usages = defs .iter() .filter_map(|&d| { @@ -455,8 +456,12 @@ fn cover_range(r0: Option, r1: Option) -> Option, token: SyntaxToken) -> FxHashSet { - sema.descend_into_macros(token) +fn find_defs( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, + offset: TextSize, +) -> FxHashSet { + sema.descend_into_macros(token, offset) .into_iter() .filter_map(|token| IdentClass::classify_token(sema, &token)) .map(IdentClass::definitions_no_ops) diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 40659e6c263..21934b9480e 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -162,9 +162,9 @@ fn hover_simple( // prefer descending the same token kind in attribute expansions, in normal macros text // equivalency is more important let descended = if in_attr { - [sema.descend_into_macros_with_kind_preference(original_token.clone())].into() + [sema.descend_into_macros_with_kind_preference(original_token.clone(), offset)].into() } else { - sema.descend_into_macros_with_same_text(original_token.clone()) + sema.descend_into_macros_with_same_text(original_token.clone(), offset) }; let descended = || descended.iter(); diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index ddc71dffa8a..d0f9f7b0e16 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -1556,6 +1556,49 @@ fn test_hover_function_show_types() { ); } +#[test] +fn test_hover_function_associated_type_params() { + check( + r#" +trait Foo { type Bar; } +impl Foo for i32 { type Bar = i64; } +fn foo(arg: ::Bar) {} +fn main() { foo$0; } +"#, + expect![[r#" + *foo* + + ```rust + test + ``` + + ```rust + fn foo(arg: ::Bar) + ``` + "#]], + ); + + check( + r#" +trait Foo { type Bar; } +impl Foo for i32 { type Bar = i32; } +fn foo(arg: <>::Bar as Foo>::Bar) {} +fn main() { foo$0; } +"#, + expect![[r#" + *foo* + + ```rust + test + ``` + + ```rust + fn foo(arg: <>::Bar as Foo>::Bar) + ``` + "#]], + ); +} + #[test] fn test_hover_function_pointer_show_identifiers() { check( @@ -3292,7 +3335,50 @@ struct S$0T(T); ``` ```rust - struct ST + struct ST + ``` + "#]], + ); +} + +#[test] +fn const_generic_default_value() { + check( + r#" +struct Foo; +struct S$0T(T); +"#, + expect![[r#" + *ST* + + ```rust + test + ``` + + ```rust + struct ST + ``` + "#]], + ); +} + +#[test] +fn const_generic_default_value_2() { + check( + r#" +struct Foo; +const VAL = 1; +struct S$0T(T); +"#, + expect![[r#" + *ST* + + ```rust + test + ``` + + ```rust + struct ST ``` "#]], ); @@ -6469,3 +6555,22 @@ fn test() { "#]], ); } + +#[test] +fn generic_params_disabled_by_cfg() { + check( + r#" +struct S<#[cfg(never)] T>; +fn test() { + let s$0: S = S; +} +"#, + expect![[r#" + *s* + + ```rust + let s: S // size = 0, align = 1 + ``` + "#]], + ); +} diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index bf77d55d58e..c9cdbff7d7d 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -484,7 +484,7 @@ impl Analysis { sysroot: Option<&OsStr>, ) -> Cancellable { self.with_db(|db| { - doc_links::external_docs(db, &position, target_dir, sysroot).unwrap_or_default() + doc_links::external_docs(db, position, target_dir, sysroot).unwrap_or_default() }) } diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 17f3771b1a5..2ca2b5b1d5f 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs @@ -99,7 +99,7 @@ pub(crate) fn moniker( }); } let navs = sema - .descend_into_macros(original_token.clone()) + .descend_into_macros(original_token.clone(), offset) .into_iter() .filter_map(|token| { IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| { diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index d1479dd1e58..0740bfbc7b1 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -175,8 +175,12 @@ impl TryToNav for FileSymbol { Some(NavigationTarget { file_id: full_range.file_id, - name: if self.is_alias { self.def.name(db)?.to_smol_str() } else { self.name.clone() }, - alias: if self.is_alias { Some(self.name.clone()) } else { None }, + name: self + .is_alias + .then(|| self.def.name(db)) + .flatten() + .map_or_else(|| self.name.clone(), |it| it.to_smol_str()), + alias: self.is_alias.then(|| self.name.clone()), kind: Some(hir::ModuleDefId::from(self.def).into()), full_range: full_range.range, focus_range, diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 813f9ed943f..2d0295692ac 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -126,7 +126,7 @@ pub(crate) fn find_defs<'a>( ) }); token.map(|token| { - sema.descend_into_macros_with_same_text(token) + sema.descend_into_macros_with_same_text(token, offset) .into_iter() .filter_map(|it| ast::NameLike::cast(it.parent()?)) .filter_map(move |name_like| { diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index 7795be54e26..847ab3d21ad 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -67,17 +67,20 @@ impl SignatureHelp { } /// Computes parameter information for the given position. -pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Option { +pub(crate) fn signature_help( + db: &RootDatabase, + FilePosition { file_id, offset }: FilePosition, +) -> Option { let sema = Semantics::new(db); - let file = sema.parse(position.file_id); + let file = sema.parse(file_id); let file = file.syntax(); let token = file - .token_at_offset(position.offset) + .token_at_offset(offset) .left_biased() // if the cursor is sandwiched between two space tokens and the call is unclosed // this prevents us from leaving the CallExpression .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?; - let token = sema.descend_into_macros_single(token); + let token = sema.descend_into_macros_single(token, offset); for node in token.parent_ancestors() { match_ast! { @@ -202,7 +205,7 @@ fn signature_help_for_call( res.signature.push('('); { if let Some((self_param, _)) = callable.receiver_param(db) { - format_to!(res.signature, "{}", self_param) + format_to!(res.signature, "{}", self_param.display(db)) } let mut buf = String::new(); for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() { @@ -1314,6 +1317,25 @@ id! { ); } + #[test] + fn fn_signature_for_method_call_defined_in_macro() { + check( + r#" +macro_rules! id { ($($tt:tt)*) => { $($tt)* } } +struct S; +id! { + impl S { + fn foo<'a>(&'a mut self) {} + } +} +fn test() { S.foo($0); } +"#, + expect![[r#" + fn foo(&'a mut self) + "#]], + ); + } + #[test] fn call_info_for_lambdas() { check( diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index ae97236409e..bb01c81d66f 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -395,10 +395,10 @@ fn traverse( NodeOrToken::Token(token) if token.kind() != COMMENT => { let token = match attr_or_derive_item { Some(AttrOrDerive::Attr(_)) => { - sema.descend_into_macros_with_kind_preference(token) + sema.descend_into_macros_with_kind_preference(token, 0.into()) } Some(AttrOrDerive::Derive(_)) | None => { - sema.descend_into_macros_single(token) + sema.descend_into_macros_single(token, 0.into()) } }; match token.parent().and_then(ast::NameLike::cast) { diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index 62b2accf5cd..7b9bb61e696 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs @@ -961,6 +961,7 @@ impl TtTreeSink<'_> { if has_pseudo_dot { assert!(right.is_empty(), "{left}.{right}"); } else { + assert!(!right.is_empty(), "{left}.{right}"); self.inner.start_node(SyntaxKind::NAME_REF); self.inner.token(SyntaxKind::INT_NUMBER, right); self.inner.finish_node(); diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index 333318f53b7..6a2a9adce15 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs @@ -184,7 +184,9 @@ pub(crate) mod entry { }; p.bump_any(); while !p.at(EOF) && !p.at(closing_paren_kind) { - expressions::expr(p); + if expressions::expr(p).is_none() { + break; + } if !p.at(EOF) && !p.at(closing_paren_kind) { p.expect(T![,]); } diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs index 8ed1c84c4c6..29d9b05d3f3 100644 --- a/crates/parser/src/grammar/generic_params.rs +++ b/crates/parser/src/grammar/generic_params.rs @@ -88,7 +88,7 @@ fn const_param(p: &mut Parser<'_>, m: Marker) { // test const_param_default_path // struct A; - generic_args::const_arg_expr(p); + generic_args::const_arg(p); } m.complete(p, CONST_PARAM); diff --git a/crates/parser/src/shortcuts.rs b/crates/parser/src/shortcuts.rs index 53cdad64992..2c47e3d086d 100644 --- a/crates/parser/src/shortcuts.rs +++ b/crates/parser/src/shortcuts.rs @@ -46,12 +46,16 @@ impl LexedStr<'_> { // Tag the token as joint if it is float with a fractional part // we use this jointness to inform the parser about what token split // event to emit when we encounter a float literal in a field access - if kind == SyntaxKind::FLOAT_NUMBER && !self.text(i).ends_with('.') { - res.was_joint(); + if kind == SyntaxKind::FLOAT_NUMBER { + if !self.text(i).ends_with('.') { + res.was_joint(); + } else { + was_joint = false; + } + } else { + was_joint = true; } } - - was_joint = true; } } res @@ -204,6 +208,7 @@ impl Builder<'_, '_> { assert!(right.is_empty(), "{left}.{right}"); self.state = State::Normal; } else { + assert!(!right.is_empty(), "{left}.{right}"); (self.sink)(StrStep::Enter { kind: SyntaxKind::NAME_REF }); (self.sink)(StrStep::Token { kind: SyntaxKind::INT_NUMBER, text: right }); (self.sink)(StrStep::Exit); diff --git a/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast index 809ad1b8d5b..49f163b164a 100644 --- a/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast +++ b/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast @@ -20,7 +20,8 @@ SOURCE_FILE IDENT "i32" WHITESPACE " " EQ "=" - WHITESPACE " " + WHITESPACE " " + CONST_ARG COMMA "," WHITESPACE " " CONST_PARAM @@ -37,8 +38,9 @@ SOURCE_FILE IDENT "i32" WHITESPACE " " EQ "=" + CONST_ARG R_ANGLE ">" SEMICOLON ";" WHITESPACE "\n" -error 23: expected a generic const argument +error 24: expected a generic const argument error 40: expected a generic const argument diff --git a/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast b/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast index 11002bf98d0..3f5fb47d287 100644 --- a/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast +++ b/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast @@ -21,16 +21,17 @@ SOURCE_FILE WHITESPACE " " EQ "=" WHITESPACE " " - PATH_EXPR - PATH + CONST_ARG + PATH_EXPR PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + COLON2 "::" PATH_SEGMENT NAME_REF - IDENT "i32" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "MAX" + IDENT "MAX" R_ANGLE ">" SEMICOLON ";" WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast b/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast index 0607ff54fbb..d6501137498 100644 --- a/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast +++ b/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast @@ -21,14 +21,15 @@ SOURCE_FILE WHITESPACE " " EQ "=" WHITESPACE " " - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE " " - LITERAL - INT_NUMBER "1" - WHITESPACE " " - R_CURLY "}" + CONST_ARG + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LITERAL + INT_NUMBER "1" + WHITESPACE " " + R_CURLY "}" R_ANGLE ">" SEMICOLON ";" WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast b/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast index 8e52313651c..6de10353bf0 100644 --- a/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast +++ b/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast @@ -21,10 +21,11 @@ SOURCE_FILE WHITESPACE " " EQ "=" WHITESPACE " " - PREFIX_EXPR - MINUS "-" - LITERAL - INT_NUMBER "1" + CONST_ARG + PREFIX_EXPR + MINUS "-" + LITERAL + INT_NUMBER "1" R_ANGLE ">" SEMICOLON ";" WHITESPACE "\n" diff --git a/crates/profile/src/stop_watch.rs b/crates/profile/src/stop_watch.rs index 71303d5a631..814a0257402 100644 --- a/crates/profile/src/stop_watch.rs +++ b/crates/profile/src/stop_watch.rs @@ -10,13 +10,13 @@ pub struct StopWatch { time: Instant, #[cfg(target_os = "linux")] counter: Option, - memory: Option, + memory: MemoryUsage, } pub struct StopWatchSpan { pub time: Duration, pub instructions: Option, - pub memory: Option, + pub memory: MemoryUsage, } impl StopWatch { @@ -45,20 +45,16 @@ impl StopWatch { None } }; + let memory = MemoryUsage::now(); let time = Instant::now(); StopWatch { time, #[cfg(target_os = "linux")] counter, - memory: None, + memory, } } - pub fn memory(mut self, yes: bool) -> StopWatch { - if yes { - self.memory = Some(MemoryUsage::now()); - } - self - } + pub fn elapsed(&mut self) -> StopWatchSpan { let time = self.time.elapsed(); @@ -69,7 +65,7 @@ impl StopWatch { #[cfg(not(target_os = "linux"))] let instructions = None; - let memory = self.memory.map(|it| MemoryUsage::now() - it); + let memory = MemoryUsage::now() - self.memory; StopWatchSpan { time, instructions, memory } } } @@ -93,9 +89,7 @@ impl fmt::Display for StopWatchSpan { } write!(f, ", {instructions}{prefix}instr")?; } - if let Some(memory) = self.memory { - write!(f, ", {memory}")?; - } + write!(f, ", {}", self.memory)?; Ok(()) } } diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index f51ea7eeb22..13463e9f72e 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -167,7 +167,8 @@ impl ProjectWorkspace { cmd.envs(&config.extra_env); cmd.arg("--version").current_dir(current_dir); cmd - })?; + }) + .with_context(|| format!("Failed to query rust toolchain version at {current_dir}, is your toolchain setup correctly?"))?; anyhow::Ok( cargo_version .get(prefix.len()..) diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 5bfac7ee45c..1f9d6db9314 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -24,7 +24,7 @@ crossbeam-channel = "0.5.5" dissimilar = "1.0.4" itertools = "0.10.5" scip = "0.1.1" -lsp-types = { version = "=0.94", features = ["proposed"] } +lsp-types = { version = "=0.94.0", features = ["proposed"] } parking_lot = "0.12.1" xflags = "0.3.0" oorandom = "11.1.3" diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index f446a7c0596..4a03be1893c 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -235,9 +235,7 @@ impl flags::AnalysisStats { if let Some(instructions) = total_span.instructions { report_metric("total instructions", instructions, "#instr"); } - if let Some(memory) = total_span.memory { - report_metric("total memory", memory.allocated.megabytes() as u64, "MB"); - } + report_metric("total memory", total_span.memory.allocated.megabytes() as u64, "MB"); if env::var("RA_COUNT").is_ok() { eprintln!("{}", profile::countme::get_all()); @@ -257,7 +255,7 @@ impl flags::AnalysisStats { eprintln!("source files: {total_file_size}, macro files: {total_macro_file_size}"); } - if self.memory_usage && verbosity.is_verbose() { + if verbosity.is_verbose() { print_memory_usage(host, vfs); } @@ -814,7 +812,7 @@ impl flags::AnalysisStats { } fn stop_watch(&self) -> StopWatch { - StopWatch::start().memory(self.memory_usage) + StopWatch::start() } } diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs index 13b7f039bb0..419440b6df7 100644 --- a/crates/rust-analyzer/src/cli/flags.rs +++ b/crates/rust-analyzer/src/cli/flags.rs @@ -62,8 +62,6 @@ xflags::xflags! { optional --randomize /// Run type inference in parallel. optional --parallel - /// Collect memory usage statistics. - optional --memory-usage /// Print the total length of all source and macro files (whitespace is not counted). optional --source-stats @@ -191,7 +189,6 @@ pub struct AnalysisStats { pub output: Option, pub randomize: bool, pub parallel: bool, - pub memory_usage: bool, pub source_stats: bool, pub only: Option, pub with_deps: bool, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index fa20c796ec2..40c50f6d176 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -150,18 +150,22 @@ config_data! { /// /// Set to `"all"` to pass `--all-features` to Cargo. check_features | checkOnSave_features: Option = "null", + /// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. + /// + /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... + check_ignore: FxHashSet = "[]", /// Specifies the working directory for running checks. /// - "workspace": run checks for workspaces in the corresponding workspaces' root directories. // FIXME: Ideally we would support this in some way - /// This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`. + /// This falls back to "root" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`. /// - "root": run checks in the project's root directory. - /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` + /// This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#` /// is set. check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"", - /// Specifies the invocation strategy to use when running the checkOnSave command. + /// Specifies the invocation strategy to use when running the check command. /// If `per_workspace` is set, the command will be executed for each workspace. /// If `once` is set, the command will be executed once. - /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` + /// This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#` /// is set. check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"", /// Whether to pass `--no-default-features` to Cargo. Defaults to @@ -1098,6 +1102,7 @@ impl Config { remap_prefix: self.data.diagnostics_remapPrefix.clone(), warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(), warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(), + check_ignore: self.data.check_ignore.clone(), } } diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index 33422fd058e..b65f38a0c71 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs @@ -6,6 +6,7 @@ use std::mem; use ide::FileId; use ide_db::FxHashMap; use nohash_hasher::{IntMap, IntSet}; +use rustc_hash::FxHashSet; use triomphe::Arc; use crate::lsp_ext; @@ -17,6 +18,7 @@ pub struct DiagnosticsMapConfig { pub remap_prefix: FxHashMap, pub warnings_as_info: Vec, pub warnings_as_hint: Vec, + pub check_ignore: FxHashSet, } #[derive(Debug, Default, Clone)] diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index e1d1130ff1b..06564578d80 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -292,6 +292,13 @@ pub(crate) fn map_rust_diagnostic_to_lsp( let mut source = String::from("rustc"); let mut code = rd.code.as_ref().map(|c| c.code.clone()); + + if let Some(code_val) = &code { + if config.check_ignore.contains(code_val) { + return Vec::new(); + } + } + if let Some(code_val) = &code { // See if this is an RFC #2103 scoped lint (e.g. from Clippy) let scoped_code: Vec<&str> = code_val.split("::").collect(); diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 138ddd20897..ea7ebd85b33 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -296,7 +296,7 @@ TypeParam = ConstParam = Attr* 'const' Name ':' Type - ('=' default_val:Expr)? + ('=' default_val:ConstArg)? LifetimeParam = Attr* Lifetime (':' TypeBoundList?)? diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 0b27faa535d..16448db04f8 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -709,7 +709,7 @@ impl ConstParam { pub fn colon_token(&self) -> Option { support::token(&self.syntax, T![:]) } pub fn ty(&self) -> Option { support::child(&self.syntax) } pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } - pub fn default_val(&self) -> Option { support::child(&self.syntax) } + pub fn default_val(&self) -> Option { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 4c6db0ef06c..17e311c0c50 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -503,11 +503,16 @@ pub fn hacky_block_expr( pub fn expr_unit() -> ast::Expr { expr_from_text("()") } + pub fn expr_literal(text: &str) -> ast::Literal { assert_eq!(text.trim(), text); ast_from_text(&format!("fn f() {{ let _ = {text}; }}")) } +pub fn expr_const_value(text: &str) -> ast::ConstArg { + ast_from_text(&format!("trait Foo {{}}")) +} + pub fn expr_empty_block() -> ast::Expr { expr_from_text("{}") } @@ -1100,7 +1105,7 @@ pub mod tokens { pub(super) static SOURCE_FILE: Lazy> = Lazy::new(|| { SourceFile::parse( - "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n", + "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n", ) }); diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 3308077da5b..691d0c618f3 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -61,6 +61,14 @@ impl ast::BlockExpr { pub fn tail_expr(&self) -> Option { self.stmt_list()?.tail_expr() } + /// Block expressions accept outer and inner attributes, but only when they are the outer + /// expression of an expression statement or the final expression of another block expression. + pub fn may_carry_attributes(&self) -> bool { + matches!( + self.syntax().parent().map(|it| it.kind()), + Some(SyntaxKind::BLOCK_EXPR | SyntaxKind::EXPR_STMT) + ) + } } #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index 4cd668a0cd5..27c8a13e58d 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs @@ -195,11 +195,16 @@ impl ast::TokenTree { // Tag the token as joint if it is float with a fractional part // we use this jointness to inform the parser about what token split // event to emit when we encounter a float literal in a field access - if kind == SyntaxKind::FLOAT_NUMBER && !t.text().ends_with('.') { - parser_input.was_joint(); + if kind == SyntaxKind::FLOAT_NUMBER { + if !t.text().ends_with('.') { + parser_input.was_joint(); + } else { + was_joint = false; + } + } else { + was_joint = true; } } - was_joint = true; } } @@ -250,6 +255,7 @@ impl ast::TokenTree { if has_pseudo_dot { assert!(right.is_empty(), "{left}.{right}"); } else { + assert!(!right.is_empty(), "{left}.{right}"); builder.start_node(SyntaxKind::NAME_REF); builder.token(SyntaxKind::INT_NUMBER, right); builder.finish_node(); diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index ea00c9540ff..71feed0f72c 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -161,23 +161,30 @@ List of features to activate. Defaults to Set to `"all"` to pass `--all-features` to Cargo. -- +[[rust-analyzer.check.ignore]]rust-analyzer.check.ignore (default: `[]`):: ++ +-- +List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. + +For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... +-- [[rust-analyzer.check.invocationLocation]]rust-analyzer.check.invocationLocation (default: `"workspace"`):: + -- Specifies the working directory for running checks. - "workspace": run checks for workspaces in the corresponding workspaces' root directories. - This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`. + This falls back to "root" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`. - "root": run checks in the project's root directory. -This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` +This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#` is set. -- [[rust-analyzer.check.invocationStrategy]]rust-analyzer.check.invocationStrategy (default: `"per_workspace"`):: + -- -Specifies the invocation strategy to use when running the checkOnSave command. +Specifies the invocation strategy to use when running the check command. If `per_workspace` is set, the command will be executed for each workspace. If `once` is set, the command will be executed once. -This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` +This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#` is set. -- [[rust-analyzer.check.noDefaultFeatures]]rust-analyzer.check.noDefaultFeatures (default: `null`):: diff --git a/editors/code/package.json b/editors/code/package.json index 76d7e91f381..44f1b81675a 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -292,6 +292,11 @@ "command": "rust-analyzer.viewMemoryLayout", "title": "View Memory Layout", "category": "rust-analyzer" + }, + { + "command": "rust-analyzer.toggleCheckOnSave", + "title": "Toggle Check on Save", + "category": "rust-analyzer" } ], "keybindings": [ @@ -700,8 +705,17 @@ } ] }, + "rust-analyzer.check.ignore": { + "markdownDescription": "List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.\n\nFor example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...", + "default": [], + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, "rust-analyzer.check.invocationLocation": { - "markdownDescription": "Specifies the working directory for running checks.\n- \"workspace\": run checks for workspaces in the corresponding workspaces' root directories.\n This falls back to \"root\" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.\n- \"root\": run checks in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", + "markdownDescription": "Specifies the working directory for running checks.\n- \"workspace\": run checks for workspaces in the corresponding workspaces' root directories.\n This falls back to \"root\" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`.\n- \"root\": run checks in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`\nis set.", "default": "workspace", "type": "string", "enum": [ @@ -714,7 +728,7 @@ ] }, "rust-analyzer.check.invocationStrategy": { - "markdownDescription": "Specifies the invocation strategy to use when running the checkOnSave command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", + "markdownDescription": "Specifies the invocation strategy to use when running the check command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`\nis set.", "default": "per_workspace", "type": "string", "enum": [ diff --git a/editors/code/src/bootstrap.ts b/editors/code/src/bootstrap.ts index ef4dff095cf..6cf399599d9 100644 --- a/editors/code/src/bootstrap.ts +++ b/editors/code/src/bootstrap.ts @@ -20,7 +20,7 @@ export async function bootstrap( log.info("Using server binary at", path); - if (!isValidExecutable(path)) { + if (!isValidExecutable(path, config.serverExtraEnv)) { if (config.serverPath) { throw new Error(`Failed to execute ${path} --version. \`config.server.path\` or \`config.serverPath\` has been set explicitly.\ Consider removing this config or making a valid server binary available at that path.`); diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index e21f536f26a..aba37bac27d 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -1407,3 +1407,10 @@ locate() ctx.pushExtCleanup(document); }; } + +export function toggleCheckOnSave(ctx: Ctx): Cmd { + return async () => { + await ctx.config.toggleCheckOnSave(); + ctx.refreshServerStatus(); + }; +} diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 0e64054c11d..39e2f767c76 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -216,6 +216,39 @@ export class Config { ), ); } + get checkOnSave() { + return this.get("checkOnSave") ?? false; + } + async toggleCheckOnSave() { + const config = this.cfg.inspect("checkOnSave") ?? { key: "checkOnSave" }; + let overrideInLanguage; + let target; + let value; + if ( + config.workspaceFolderValue !== undefined || + config.workspaceFolderLanguageValue !== undefined + ) { + target = vscode.ConfigurationTarget.WorkspaceFolder; + overrideInLanguage = config.workspaceFolderLanguageValue; + value = config.workspaceFolderValue || config.workspaceFolderLanguageValue; + } else if ( + config.workspaceValue !== undefined || + config.workspaceLanguageValue !== undefined + ) { + target = vscode.ConfigurationTarget.Workspace; + overrideInLanguage = config.workspaceLanguageValue; + value = config.workspaceValue || config.workspaceLanguageValue; + } else if (config.globalValue !== undefined || config.globalLanguageValue !== undefined) { + target = vscode.ConfigurationTarget.Global; + overrideInLanguage = config.globalLanguageValue; + value = config.globalValue || config.globalLanguageValue; + } else if (config.defaultValue !== undefined || config.defaultLanguageValue !== undefined) { + overrideInLanguage = config.defaultLanguageValue; + value = config.defaultValue || config.defaultLanguageValue; + } + await this.cfg.update("checkOnSave", !(value || false), target || null, overrideInLanguage); + } + get traceExtension() { return this.get("trace.extension"); } diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 16c14ca54f2..363a7a82e68 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -94,6 +94,7 @@ export class Ctx { private unlinkedFiles: vscode.Uri[]; private _dependencies: RustDependenciesProvider | undefined; private _treeView: vscode.TreeView | undefined; + private lastStatus: ServerStatusParams | { health: "stopped" } = { health: "stopped" }; get client() { return this._client; @@ -404,7 +405,15 @@ export class Ctx { } setServerStatus(status: ServerStatusParams | { health: "stopped" }) { + this.lastStatus = status; + this.updateStatusBarItem(); + } + refreshServerStatus() { + this.updateStatusBarItem(); + } + private updateStatusBarItem() { let icon = ""; + const status = this.lastStatus; const statusBar = this.statusBar; statusBar.show(); statusBar.tooltip = new vscode.MarkdownString("", true); @@ -447,13 +456,18 @@ export class Ctx { "statusBarItem.warningBackground", ); statusBar.command = "rust-analyzer.startServer"; - statusBar.text = `$(stop-circle) rust-analyzer`; + statusBar.text = "$(stop-circle) rust-analyzer"; return; } if (statusBar.tooltip.value) { statusBar.tooltip.appendMarkdown("\n\n---\n\n"); } - statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)"); + statusBar.tooltip.appendMarkdown("\n\n[Open Logs](command:rust-analyzer.openLogs)"); + statusBar.tooltip.appendMarkdown( + `\n\n[${ + this.config.checkOnSave ? "Disable" : "Enable" + } Check on Save](command:rust-analyzer.toggleCheckOnSave)`, + ); statusBar.tooltip.appendMarkdown( "\n\n[Reload Workspace](command:rust-analyzer.reloadWorkspace)", ); diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 448150bac06..ee5e5b1b80c 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -180,6 +180,7 @@ function createCommands(): Record { ssr: { enabled: commands.ssr }, serverVersion: { enabled: commands.serverVersion }, viewMemoryLayout: { enabled: commands.viewMemoryLayout }, + toggleCheckOnSave: { enabled: commands.toggleCheckOnSave }, // Internal commands which are invoked by the server. applyActionGroup: { enabled: commands.applyActionGroup }, applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand }, diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 38ce6761578..0414ea0f806 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts @@ -2,6 +2,7 @@ import * as vscode from "vscode"; import { strict as nativeAssert } from "assert"; import { exec, type ExecOptions, spawnSync } from "child_process"; import { inspect } from "util"; +import type { Env } from "./client"; export function assert(condition: boolean, explanation: string): asserts condition { try { @@ -93,10 +94,13 @@ export function isDocumentInWorkspace(document: RustDocument): boolean { return false; } -export function isValidExecutable(path: string): boolean { +export function isValidExecutable(path: string, extraEnv: Env): boolean { log.debug("Checking availability of a binary at", path); - const res = spawnSync(path, ["--version"], { encoding: "utf8" }); + const res = spawnSync(path, ["--version"], { + encoding: "utf8", + env: { ...process.env, ...extraEnv }, + }); const printOutput = res.error ? log.warn : log.info; printOutput(path, "--version:", res); @@ -151,6 +155,7 @@ export function execute(command: string, options: ExecOptions): Promise } export function executeDiscoverProject(command: string, options: ExecOptions): Promise { + options = Object.assign({ maxBuffer: 10 * 1024 * 1024 }, options); log.info(`running command: ${command}`); return new Promise((resolve, reject) => { exec(command, options, (err, stdout, _) => { diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml index 01707d30191..e1c49db39d5 100644 --- a/lib/lsp-server/Cargo.toml +++ b/lib/lsp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-server" -version = "0.7.2" +version = "0.7.3" description = "Generic LSP server scaffold." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server" @@ -9,7 +9,8 @@ edition = "2021" [dependencies] log = "0.4.17" serde_json = "1.0.96" -serde = { version = "1.0.156", features = ["derive"] } +# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde +serde = { version = "1.0.156, < 1.0.172", features = ["derive"] } crossbeam-channel = "0.5.6" [dev-dependencies] diff --git a/lib/lsp-server/src/stdio.rs b/lib/lsp-server/src/stdio.rs index 49a825e579b..e487b9b4622 100644 --- a/lib/lsp-server/src/stdio.rs +++ b/lib/lsp-server/src/stdio.rs @@ -3,6 +3,8 @@ use std::{ thread, }; +use log::debug; + use crossbeam_channel::{bounded, Receiver, Sender}; use crate::Message; @@ -23,7 +25,8 @@ pub(crate) fn stdio_transport() -> (Sender, Receiver, IoThread while let Some(msg) = Message::read(&mut stdin)? { let is_exit = matches!(&msg, Message::Notification(n) if n.is_exit()); - reader_sender.send(msg).unwrap(); + debug!("sending message {:#?}", msg); + reader_sender.send(msg).expect("receiver was dropped, failed to send a message"); if is_exit { break; diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs index 68537423195..e4710260409 100644 --- a/xtask/src/metrics.rs +++ b/xtask/src/metrics.rs @@ -103,9 +103,7 @@ impl Metrics { path: &str, ) -> anyhow::Result<()> { eprintln!("\nMeasuring analysis-stats/{name}"); - let output = - cmd!(sh, "./target/release/rust-analyzer -q analysis-stats --memory-usage {path}") - .read()?; + let output = cmd!(sh, "./target/release/rust-analyzer -q analysis-stats {path}").read()?; for (metric, value, unit) in parse_metrics(&output) { self.report(&format!("analysis-stats/{name}/{metric}"), value, unit.into()); } From 343ee8bacf6f4f26dd4932a3effa1785548a2e56 Mon Sep 17 00:00:00 2001 From: Alex Kladov Date: Mon, 21 Aug 2023 14:29:18 +0100 Subject: [PATCH 024/250] internal: unpin serde Sered no longer uses blobs as of https://github.com/serde-rs/serde/pull/2590 As such, there's no longer need for us to pin it. Note that this doesn't upgrade serde version we use: I am fairly confident that the blobs are already there are fine, and now I am fairly confident that all future versions of serde will be fine as well. --- Cargo.lock | 10 +++++----- Cargo.toml | 3 +-- lib/lsp-server/Cargo.toml | 5 ++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e838fcc880..30e1633911c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1000,22 +1000,22 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "lsp-server" version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72417faa455bfb4e5bf14b157d8e2ca2ed74b4e89b8cf42ea2d864825ae5c8a2" dependencies = [ "crossbeam-channel", "log", - "lsp-types", "serde", "serde_json", ] [[package]] name = "lsp-server" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72417faa455bfb4e5bf14b157d8e2ca2ed74b4e89b8cf42ea2d864825ae5c8a2" +version = "0.7.4" dependencies = [ "crossbeam-channel", "log", + "lsp-types", "serde", "serde_json", ] @@ -1555,7 +1555,7 @@ dependencies = [ "ide-ssr", "itertools", "load-cargo", - "lsp-server 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lsp-server 0.7.3", "lsp-types", "mbe", "mimalloc", diff --git a/Cargo.toml b/Cargo.toml index 5eb59d6db11..e96bba2b655 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,8 +97,7 @@ smallvec = { version = "1.10.0", features = [ smol_str = "0.2.0" nohash-hasher = "0.2.0" text-size = "1.1.0" -# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde -serde = { version = "1.0.156, < 1.0.172", features = ["derive"] } +serde = { version = "1.0.156", features = ["derive"] } serde_json = "1.0.96" triomphe = { version = "0.1.8", default-features = false, features = ["std"] } # can't upgrade due to dashmap depending on 0.12.3 currently diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml index e1c49db39d5..7ec3247e943 100644 --- a/lib/lsp-server/Cargo.toml +++ b/lib/lsp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-server" -version = "0.7.3" +version = "0.7.4" description = "Generic LSP server scaffold." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server" @@ -9,8 +9,7 @@ edition = "2021" [dependencies] log = "0.4.17" serde_json = "1.0.96" -# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde -serde = { version = "1.0.156, < 1.0.172", features = ["derive"] } +serde = { version = "1.0.156", features = ["derive"] } crossbeam-channel = "0.5.6" [dev-dependencies] From 6caf79c36e517ab9d59f2fabd2013eb5aa0d9778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 21 Aug 2023 22:07:05 +0300 Subject: [PATCH 025/250] Allow internal_features in test --- crates/rust-analyzer/tests/slow-tests/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index 0bb29e7080f..ed6ef47c8e0 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -861,6 +861,7 @@ edition = "2021" bar = {path = "../bar"} //- /foo/src/main.rs +#![allow(internal_features)] #![feature(rustc_attrs, decl_macro)] use bar::Bar; @@ -938,7 +939,7 @@ pub fn foo(_input: TokenStream) -> TokenStream { let res = server.send_request::(HoverParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("foo/src/main.rs"), - Position::new(11, 9), + Position::new(12, 9), ), work_done_progress_params: Default::default(), }); From 7012fff9abf6d63625cae368064097966f204bcd Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 21 Aug 2023 20:43:04 +0000 Subject: [PATCH 026/250] Fix elided lifetimes in rust-lang/rust --- crates/test-utils/src/fixture.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/test-utils/src/fixture.rs b/crates/test-utils/src/fixture.rs index 75e7a3fec00..3f8b5a08969 100644 --- a/crates/test-utils/src/fixture.rs +++ b/crates/test-utils/src/fixture.rs @@ -313,7 +313,7 @@ impl FixtureWithProjectMeta { } impl MiniCore { - const RAW_SOURCE: &str = include_str!("./minicore.rs"); + const RAW_SOURCE: &'static str = include_str!("./minicore.rs"); fn has_flag(&self, flag: &str) -> bool { self.activated_flags.iter().any(|it| it == flag) From b4576b52b7eeb657f33dfb8e496a3d0d6a573a5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 22 Aug 2023 09:26:04 +0300 Subject: [PATCH 027/250] Run analysis-stats on stable --- .github/workflows/ci.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9f246098e76..ec3128d9ec1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -86,6 +86,12 @@ jobs: - name: Test run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet + - name: Switch to stable toolchain + run: | + rustup update --no-self-update stable + rustup component add --toolchain stable rust-src + rustup default stable + - name: Run analysis-stats on rust-analyzer if: matrix.os == 'ubuntu-latest' run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats . From 43e868807e0dd539d97a841bbbb49ba8cbfc2caf Mon Sep 17 00:00:00 2001 From: Alex Kladov Date: Tue, 22 Aug 2023 11:42:53 +0100 Subject: [PATCH 028/250] internal: up lsp-server --- Cargo.lock | 10 +++++----- Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15b0e92d92d..cf364d28de0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -999,12 +999,11 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "lsp-server" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72417faa455bfb4e5bf14b157d8e2ca2ed74b4e89b8cf42ea2d864825ae5c8a2" +version = "0.7.4" dependencies = [ "crossbeam-channel", "log", + "lsp-types", "serde", "serde_json", ] @@ -1012,10 +1011,11 @@ dependencies = [ [[package]] name = "lsp-server" version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b52dccdf3302eefab8c8a1273047f0a3c3dca4b527c8458d00c09484c8371928" dependencies = [ "crossbeam-channel", "log", - "lsp-types", "serde", "serde_json", ] @@ -1555,7 +1555,7 @@ dependencies = [ "ide-ssr", "itertools", "load-cargo", - "lsp-server 0.7.3", + "lsp-server 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", "mbe", "mimalloc", diff --git a/Cargo.toml b/Cargo.toml index e96bba2b655..e97e58f5276 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,7 @@ proc-macro-test = { path = "./crates/proc-macro-test" } # In-tree crates that are published separately and follow semver. See lib/README.md line-index = { version = "0.1.0-pre.1" } la-arena = { version = "0.3.1" } -lsp-server = { version = "0.7.3" } +lsp-server = { version = "0.7.4" } # non-local crates smallvec = { version = "1.10.0", features = [ From bc42b9911dcce9a426afc59179d9e2f413e1f411 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Tue, 22 Aug 2023 18:51:15 -0700 Subject: [PATCH 029/250] SCIP: Report the correct version of rust-analyzer in the metadata Previously this was hard coded to "0.1". The SCIP protocol allows this to be an arbitrary string: ``` message ToolInfo { // Name of the indexer that produced this index. string name = 1; // Version of the indexer that produced this index. string version = 2; // Command-line arguments that were used to invoke this indexer. repeated string arguments = 3; } ``` so use the same string reported by `rust-analyzer --version`. --- crates/rust-analyzer/src/cli/scip.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index 44337f955e5..8c056fff000 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -51,7 +51,7 @@ impl flags::Scip { version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(), tool_info: Some(scip_types::ToolInfo { name: "rust-analyzer".to_owned(), - version: "0.1".to_owned(), + version: format!("{}", crate::version::version()), arguments: vec![], special_fields: Default::default(), }) From 4e034d78de1b981aacba87e5b88acb176ba5019e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Fri, 25 Aug 2023 18:01:46 +0300 Subject: [PATCH 030/250] Set RUSTC_BOOTSTRAP=1 when running analysis-stats on libstd --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ec3128d9ec1..479ca235408 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -98,7 +98,7 @@ jobs: - name: Run analysis-stats on rust std library if: matrix.os == 'ubuntu-latest' - run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std + run: RUSTC_BOOTSTRAP=1 target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std # Weird targets to catch non-portable code rust-cross: From 3864b43d2829b888f88d79892511f2feb0773b60 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 25 Aug 2023 15:39:06 +0330 Subject: [PATCH 031/250] Update offset intrinsic to match 1.72 --- .../hir-ty/src/consteval/tests/intrinsics.rs | 18 +-- crates/hir-ty/src/mir/eval/shim.rs | 128 +++++++++++++----- 2 files changed, 107 insertions(+), 39 deletions(-) diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs index 2855f789001..cc3a43fd9ae 100644 --- a/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -499,24 +499,26 @@ fn offset() { r#" //- minicore: coerce_unsized, index, slice extern "rust-intrinsic" { - pub fn offset(dst: *const T, offset: isize) -> *const T; + pub fn offset(dst: Ptr, offset: Delta) -> Ptr; + pub fn arith_offset(dst: *const T, offset: isize) -> *const T; } - const GOAL: u8 = unsafe { - let ar: &[(u8, u8, u8)] = &[ + const GOAL: i32 = unsafe { + let ar: &[(i32, i32, i32)] = &[ (10, 11, 12), (20, 21, 22), (30, 31, 32), (40, 41, 42), (50, 51, 52), ]; - let ar: *const [(u8, u8, u8)] = ar; - let ar = ar as *const (u8, u8, u8); - let element = *offset(ar, 2); - element.1 + let ar: *const [(i32, i32, i32)] = ar; + let ar = ar as *const (i32, i32, i32); + let element3 = *offset(ar, 2usize); + let element4 = *arith_offset(ar, 3); + element3.1 * 100 + element4.0 }; "#, - 31, + 3140, ); } diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 52943e97ac0..18396638940 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -4,7 +4,10 @@ use std::cmp; use chalk_ir::TyKind; -use hir_def::resolver::HasResolver; +use hir_def::{ + builtin_type::{BuiltinInt, BuiltinUint}, + resolver::HasResolver, +}; use hir_expand::mod_path::ModPath; use super::*; @@ -300,21 +303,36 @@ impl Evaluator<'_> { BeginPanic => Err(MirEvalError::Panic("".to_string())), PanicFmt => { let message = (|| { - let resolver = self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db.upcast()); + let resolver = self + .db + .crate_def_map(self.crate_id) + .crate_root() + .resolver(self.db.upcast()); let Some(format_fn) = resolver.resolve_path_in_value_ns_fully( self.db.upcast(), - &hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments( - hir_expand::mod_path::PathKind::Abs, - [name![std], name![fmt], name![format]].into_iter(), - )), + &hir_def::path::Path::from_known_path_with_no_generic( + ModPath::from_segments( + hir_expand::mod_path::PathKind::Abs, + [name![std], name![fmt], name![format]].into_iter(), + ), + ), ) else { not_supported!("std::fmt::format not found"); }; - let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { not_supported!("std::fmt::format is not a function") }; - let message_string = self.interpret_mir(self.db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, args.map(|x| IntervalOrOwned::Owned(x.clone())))?; - let addr = Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?; + let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { + not_supported!("std::fmt::format is not a function") + }; + let message_string = self.interpret_mir( + self.db + .mir_body(format_fn.into()) + .map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, + args.map(|x| IntervalOrOwned::Owned(x.clone())), + )?; + let addr = + Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?; let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]); - Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?).into_owned()) + Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?) + .into_owned()) })() .unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}")); Err(MirEvalError::Panic(message)) @@ -483,9 +501,7 @@ impl Evaluator<'_> { } "syscall" => { let Some((id, rest)) = args.split_first() else { - return Err(MirEvalError::TypeError( - "syscall arg1 is not provided", - )); + return Err(MirEvalError::TypeError("syscall arg1 is not provided")); }; let id = from_bytes!(i64, id.get(self)?); self.exec_syscall(id, rest, destination, locals, span) @@ -710,7 +726,8 @@ impl Evaluator<'_> { } match name { "size_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("size_of generic arg is not provided")); }; @@ -718,14 +735,17 @@ impl Evaluator<'_> { destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size]) } "min_align_of" | "pref_align_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { return Err(MirEvalError::TypeError("align_of generic arg is not provided")); }; let align = self.layout(ty)?.align.abi.bytes(); destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) } "size_of_val" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("size_of_val generic arg is not provided")); }; @@ -741,8 +761,12 @@ impl Evaluator<'_> { } } "min_align_of_val" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { - return Err(MirEvalError::TypeError("min_align_of_val generic arg is not provided")); + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError( + "min_align_of_val generic arg is not provided", + )); }; let [arg] = args else { return Err(MirEvalError::TypeError("min_align_of_val args are not provided")); @@ -756,7 +780,8 @@ impl Evaluator<'_> { } } "type_name" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("type_name generic arg is not provided")); }; @@ -779,7 +804,8 @@ impl Evaluator<'_> { .write_from_bytes(self, &len.to_le_bytes()) } "needs_drop" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("size_of generic arg is not provided")); }; @@ -831,9 +857,12 @@ impl Evaluator<'_> { let lhs = i128::from_le_bytes(pad16(lhs.get(self)?, false)); let rhs = i128::from_le_bytes(pad16(rhs.get(self)?, false)); let ans = lhs.wrapping_sub(rhs); - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { - return Err(MirEvalError::TypeError("ptr_offset_from generic arg is not provided")); + return Err(MirEvalError::TypeError( + "ptr_offset_from generic arg is not provided", + )); }; let size = self.size_of_sized(ty, locals, "ptr_offset_from arg")? as i128; let ans = ans / size; @@ -940,7 +969,8 @@ impl Evaluator<'_> { "copy_nonoverlapping args are not provided", )); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError( "copy_nonoverlapping generic arg is not provided", @@ -959,9 +989,45 @@ impl Evaluator<'_> { let [ptr, offset] = args else { return Err(MirEvalError::TypeError("offset args are not provided")); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) - else { - return Err(MirEvalError::TypeError("offset generic arg is not provided")); + let ty = if name == "offset" { + let Some(ty0) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError("offset generic arg is not provided")); + }; + let Some(ty1) = + generic_args.as_slice(Interner).get(1).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError("offset generic arg is not provided")); + }; + if !matches!( + ty1.as_builtin(), + Some( + BuiltinType::Int(BuiltinInt::Isize) + | BuiltinType::Uint(BuiltinUint::Usize) + ) + ) { + return Err(MirEvalError::TypeError( + "offset generic arg is not usize or isize", + )); + } + match ty0.as_raw_ptr() { + Some((ty, _)) => ty, + None => { + return Err(MirEvalError::TypeError( + "offset generic arg is not a raw pointer", + )); + } + } + } else { + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError( + "arith_offset generic arg is not provided", + )); + }; + ty }; let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false)); let offset = u128::from_le_bytes(pad16(offset.get(self)?, false)); @@ -1079,7 +1145,8 @@ impl Evaluator<'_> { let [arg] = args else { return Err(MirEvalError::TypeError("discriminant_value arg is not provided")); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError( "discriminant_value generic arg is not provided", @@ -1139,11 +1206,10 @@ impl Evaluator<'_> { }; let count = from_bytes!(usize, count.get(self)?); let val = from_bytes!(u8, val.get(self)?); - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { - return Err(MirEvalError::TypeError( - "write_bytes generic arg is not provided", - )); + return Err(MirEvalError::TypeError("write_bytes generic arg is not provided")); }; let dst = Address::from_bytes(dst.get(self)?)?; let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?; From fa76f60cc15bfee251366476b982e4f9a97e199a Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 25 Aug 2023 18:08:36 +0330 Subject: [PATCH 032/250] Run cargo fmt on 1.72 --- crates/hir-def/src/nameres/collector.rs | 6 +++++- crates/hir-expand/src/builtin_fn_macro.rs | 2 +- crates/hir-expand/src/hygiene.rs | 2 +- crates/hir-ty/src/infer/cast.rs | 12 +++++++++--- crates/hir-ty/src/layout.rs | 3 ++- crates/hir-ty/src/mir/eval/shim/simd.rs | 4 +++- crates/hir-ty/src/mir/lower.rs | 17 +++++++---------- 7 files changed, 28 insertions(+), 18 deletions(-) diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index e9e71a8747f..2d4586146db 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -1828,7 +1828,11 @@ impl ModCollector<'_, '_> { let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else { // `#[macro_use]` (without any paths) found, forget collected names and just import // all visible macros. - self.def_collector.import_macros_from_extern_crate(target_crate, None, Some(extern_crate_id)); + self.def_collector.import_macros_from_extern_crate( + target_crate, + None, + Some(extern_crate_id), + ); return; }; for path in paths { diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 95c6baf42da..6a48acef963 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -692,7 +692,7 @@ pub(crate) fn include_arg_to_tt( arg_id: MacroCallId, ) -> Result<(triomphe::Arc<(::tt::Subtree<::tt::TokenId>, TokenMap)>, FileId), ExpandError> { let loc = db.lookup_intern_macro_call(arg_id); - let Some(EagerCallInfo { arg,arg_id, .. }) = loc.eager.as_deref() else { + let Some(EagerCallInfo { arg, arg_id, .. }) = loc.eager.as_deref() else { panic!("include_arg_to_tt called on non include macro call: {:?}", &loc.eager); }; let path = parse_string(&arg.0)?; diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs index ade4a592893..ca65db1136c 100644 --- a/crates/hir-expand/src/hygiene.rs +++ b/crates/hir-expand/src/hygiene.rs @@ -242,7 +242,7 @@ impl HygieneFrame { krate, call_site: None, def_site: None, - } + }; }; let def_site = info.attr_input_or_mac_def_start.map(|it| db.hygiene_frame(it.file_id)); diff --git a/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs index 9e1c74b16fa..a116d444731 100644 --- a/crates/hir-ty/src/infer/cast.rs +++ b/crates/hir-ty/src/infer/cast.rs @@ -39,8 +39,14 @@ impl CastCheck { } fn check_ref_to_ptr_cast(expr_ty: Ty, cast_ty: Ty, table: &mut InferenceTable<'_>) -> bool { - let Some((expr_inner_ty, _, _)) = expr_ty.as_reference() else { return false; }; - let Some((cast_inner_ty, _)) = cast_ty.as_raw_ptr() else { return false; }; - let TyKind::Array(expr_elt_ty, _) = expr_inner_ty.kind(Interner) else { return false; }; + let Some((expr_inner_ty, _, _)) = expr_ty.as_reference() else { + return false; + }; + let Some((cast_inner_ty, _)) = cast_ty.as_raw_ptr() else { + return false; + }; + let TyKind::Array(expr_elt_ty, _) = expr_inner_ty.kind(Interner) else { + return false; + }; table.coerce(expr_elt_ty, cast_inner_ty).is_ok() } diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index b15339d4434..714930ba667 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -109,7 +109,8 @@ fn layout_of_simd_ty( // * the homogeneous field type and the number of fields. let (e_ty, e_len, is_array) = if let TyKind::Array(e_ty, _) = f0_ty.kind(Interner) { // Extract the number of elements from the layout of the array field: - let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), env.clone())?.fields else { + let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), env.clone())?.fields + else { user_error!("Array with non array layout"); }; diff --git a/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs index ec746310487..51900662426 100644 --- a/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -45,7 +45,9 @@ impl Evaluator<'_> { }; match try_const_usize(self.db, len) { Some(len) => { - let Some(ty) = subst.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { + let Some(ty) = + subst.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { return Err(MirEvalError::TypeError("simd type with no ty param")); }; Ok((len as usize, ty.clone())) diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 51cf882d053..7c15aa42fd2 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -1201,7 +1201,8 @@ impl<'ctx> MirLowerCtx<'ctx> { let Some(values) = elements .iter() .map(|it| { - let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)? else { + let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)? + else { return Ok(None); }; current = c; @@ -1259,7 +1260,8 @@ impl<'ctx> MirLowerCtx<'ctx> { *expr, rhs.project(ProjectionElem::TupleOrClosureField(i)), span, - )? else { + )? + else { return Ok(None); }; current = c; @@ -1268,8 +1270,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Underscore => Ok(Some(current)), _ => { - let Some((lhs_place, current)) = - self.lower_expr_as_place(current, lhs, false)? + let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)? else { return Ok(None); }; @@ -1286,9 +1287,7 @@ impl<'ctx> MirLowerCtx<'ctx> { rhs: ExprId, span: MirSpan, ) -> Result> { - let Some((rhs_op, current)) = - self.lower_expr_to_some_operand(rhs, current)? - else { + let Some((rhs_op, current)) = self.lower_expr_to_some_operand(rhs, current)? else { return Ok(None); }; if matches!(&self.body.exprs[lhs], Expr::Underscore) { @@ -1303,9 +1302,7 @@ impl<'ctx> MirLowerCtx<'ctx> { self.push_assignment(current, temp.clone(), rhs_op.into(), span); return self.lower_destructing_assignment(current, lhs, temp, span); } - let Some((lhs_place, current)) = - self.lower_expr_as_place(current, lhs, false)? - else { + let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)? else { return Ok(None); }; self.push_assignment(current, lhs_place, rhs_op.into(), span); From 204bc2cb60c83814e1fbd85fbbc64ecfe846a5e8 Mon Sep 17 00:00:00 2001 From: xffxff <1247714429@qq.com> Date: Sat, 26 Aug 2023 10:41:19 +0800 Subject: [PATCH 033/250] fix: diagnostics for 'while let' loop with label in condition --- crates/hir-def/src/body/lower.rs | 22 ++++++++++++++++++- .../src/handlers/undeclared_label.rs | 19 ++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 7071fcb9394..6a2ac15465f 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -744,7 +744,27 @@ impl ExprCollector<'_> { fn collect_while_loop(&mut self, syntax_ptr: AstPtr, e: ast::WhileExpr) -> ExprId { let label = e.label().map(|label| self.collect_label(label)); let body = self.collect_labelled_block_opt(label, e.loop_body()); - let condition = self.collect_expr_opt(e.condition()); + + // Labels can also be used in the condition expression, like this: + // ``` + // fn main() { + // let mut optional = Some(0); + // 'my_label: while let Some(a) = match optional { + // None => break 'my_label, + // Some(val) => Some(val), + // } { + // println!("{}", a); + // optional = None; + // } + // } + // ``` + let condition = match label { + Some(label) => { + self.with_labeled_rib(label, |this| this.collect_expr_opt(e.condition())) + } + None => self.collect_expr_opt(e.condition()), + }; + let break_expr = self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr.clone()); let if_expr = self.alloc_expr( diff --git a/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/crates/ide-diagnostics/src/handlers/undeclared_label.rs index 7de9a9a323e..495ea748776 100644 --- a/crates/ide-diagnostics/src/handlers/undeclared_label.rs +++ b/crates/ide-diagnostics/src/handlers/undeclared_label.rs @@ -34,6 +34,25 @@ fn foo() { ); } + #[test] + fn while_let_loop_with_label_in_condition() { + check_diagnostics( + r#" +fn foo() { + let mut optional = Some(0); + + 'my_label: while let Some(a) = match optional { + None => break 'my_label, + Some(val) => Some(val), + } { + optional = None; + continue 'my_label; + } +} +"#, + ); + } + #[test] fn for_loop() { check_diagnostics( From b96db2273bc0fc25b29ee238e6fb713efc5a9379 Mon Sep 17 00:00:00 2001 From: The 8472 Date: Sat, 26 Aug 2023 14:54:00 +0200 Subject: [PATCH 034/250] Document std limitations before/after main --- library/std/src/lib.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 1955ef815ff..048e8921d17 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -152,6 +152,31 @@ //! contains further primitive shared memory types, including [`atomic`] and //! [`mpsc`], which contains the channel types for message passing. //! +//! # Use before and after `main()` +//! +//! Many parts of the standard library are expected to work before and after `main()`; +//! but this is not guaranteed or ensured by tests. It is recommended that you write your own tests +//! and run them on each platform you wish to support. +//! This means that use of `std` before/after main, especially of features that interact with the +//! OS or global state, is exempted from stability and portability guarantees and instead only +//! provided on a best-effort basis. Nevertheless bug reports are appreciated. +//! +//! On the other hand `core` and `alloc` are most likely to work in such environments with +//! the caveat that any hookable behavior such as panics, oom handling or allocators will also +//! depend on the compatibility of the hooks. +//! +//! Some features may also behave differently outside main, e.g. stdio could become unbuffered, +//! some panics might turn into aborts, backtraces might not get symbolicated or similar. +//! +//! Non-exhaustive list of known limitations: +//! +//! - after-main use of thread-locals, which also affects additional features: +//! - [`thread::current()`] +//! - [`thread::scope()`] +//! - [`sync::mpsc`] +//! - before-main stdio file descriptors are not guaranteed to be open on unix platforms +//! +//! //! [I/O]: io //! [`MIN`]: i32::MIN //! [`MAX`]: i32::MAX @@ -187,7 +212,6 @@ //! [rust-discord]: https://discord.gg/rust-lang //! [array]: prim@array //! [slice]: prim@slice - // To run std tests without x.py without ending up with two copies of std, Miri needs to be // able to "empty" this crate. See . // rustc itself never sets the feature, so this line has no effect there. From a0d27610ac1e8767615bffb70a41b947b0cf58d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 28 Aug 2023 10:22:33 +0300 Subject: [PATCH 035/250] Use env node to set RUSTC_BOOTSTRAP --- .github/workflows/ci.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 479ca235408..fb7b4b07f98 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -98,7 +98,9 @@ jobs: - name: Run analysis-stats on rust std library if: matrix.os == 'ubuntu-latest' - run: RUSTC_BOOTSTRAP=1 target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std + env: + RUSTC_BOOTSTRAP: 1 + run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std # Weird targets to catch non-portable code rust-cross: From a6f53567b092be24a5425e8d575175c8c89a03c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 28 Aug 2023 10:23:24 +0300 Subject: [PATCH 036/250] Fix release workflow --- .github/workflows/release.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 43681c785fd..b5dbe30c322 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -128,6 +128,8 @@ jobs: - name: Run analysis-stats on rust std library if: matrix.target == 'x86_64-unknown-linux-gnu' + env: + RUSTC_BOOTSTRAP: 1 run: target/${{ matrix.target }}/release/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std - name: Upload artifacts From e457759cbb6905c0078059a0bb8a23fdf9aac1eb Mon Sep 17 00:00:00 2001 From: vsrs Date: Mon, 28 Aug 2023 15:23:20 +0700 Subject: [PATCH 037/250] Add bind_unused_param assistant. --- .../src/handlers/bind_unused_param.rs | 130 ++++++++++++++++++ .../src/handlers/remove_unused_param.rs | 16 ++- crates/ide-assists/src/lib.rs | 2 + crates/ide-assists/src/tests/generated.rs | 15 ++ 4 files changed, 156 insertions(+), 7 deletions(-) create mode 100644 crates/ide-assists/src/handlers/bind_unused_param.rs diff --git a/crates/ide-assists/src/handlers/bind_unused_param.rs b/crates/ide-assists/src/handlers/bind_unused_param.rs new file mode 100644 index 00000000000..0a22d26193a --- /dev/null +++ b/crates/ide-assists/src/handlers/bind_unused_param.rs @@ -0,0 +1,130 @@ +use crate::assist_context::{AssistContext, Assists}; +use ide_db::{ + assists::{AssistId, AssistKind}, + defs::Definition, + LineIndexDatabase, +}; +use syntax::{ + ast::{self, edit_in_place::Indent}, + AstNode, +}; + +use super::remove_unused_param::is_trait_impl; + +// Assist: bind_unused_param +// +// Binds unused function parameter to an underscore. +// +// ``` +// fn some_function(x: i32$0) {} +// ``` +// -> +// ``` +// fn some_function(x: i32) { +// let _ = x; +// } +// ``` +pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let param: ast::Param = ctx.find_node_at_offset()?; + + let ident_pat = match param.pat()? { + ast::Pat::IdentPat(it) => it, + _ => return None, + }; + + let func = param.syntax().ancestors().find_map(ast::Fn::cast)?; + if is_trait_impl(&func) { + cov_mark::hit!(trait_impl); + return None; + } + + let param_def = { + let local = ctx.sema.to_def(&ident_pat)?; + Definition::Local(local) + }; + if param_def.usages(&ctx.sema).at_least_one() { + cov_mark::hit!(keep_used); + return None; + } + + let line_index = ctx.db().line_index(ctx.file_id()); + + let mut indent = func.indent_level(); + indent.0 += 1; + let mut text = format!("\n{indent}let _ = {ident_pat};"); + + let stmt_list = func.body()?.stmt_list()?; + let l_curly_range = stmt_list.l_curly_token()?.text_range(); + let r_curly_range = stmt_list.r_curly_token()?.text_range(); + let left_line = line_index.line_col(l_curly_range.end()).line; + let right_line = line_index.line_col(r_curly_range.start()).line; + + if left_line == right_line { + text.push('\n'); + } + + acc.add( + AssistId("bind_unused_param", AssistKind::Refactor), + &format!("Bind as `let _ = {};`", &ident_pat), + param.syntax().text_range(), + |builder| { + builder.insert(l_curly_range.end(), text); + }, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_assist; + + use super::*; + + #[test] + fn bind_unused_empty_block() { + check_assist( + bind_unused_param, + r#" +fn foo($0y: i32) {} +"#, + r#" +fn foo(y: i32) { + let _ = y; +} +"#, + ); + } + + #[test] + fn bind_unused_empty_block_with_newline() { + check_assist( + bind_unused_param, + r#" +fn foo($0y: i32) { +} +"#, + r#" +fn foo(y: i32) { + let _ = y; +} +"#, + ); + } + + #[test] + fn bind_unused_generic() { + check_assist( + bind_unused_param, + r#" +fn foo($0y: T) +where T : Default { +} +"#, + r#" +fn foo(y: T) +where T : Default { + let _ = y; +} +"#, + ); + } +} diff --git a/crates/ide-assists/src/handlers/remove_unused_param.rs b/crates/ide-assists/src/handlers/remove_unused_param.rs index 0772b168d49..aa1002ee393 100644 --- a/crates/ide-assists/src/handlers/remove_unused_param.rs +++ b/crates/ide-assists/src/handlers/remove_unused_param.rs @@ -42,13 +42,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> param.syntax().parent()?.children().find_map(ast::SelfParam::cast).is_some(); // check if fn is in impl Trait for .. - if func - .syntax() - .parent() // AssocItemList - .and_then(|x| x.parent()) - .and_then(ast::Impl::cast) - .map_or(false, |imp| imp.trait_().is_some()) - { + if is_trait_impl(&func) { cov_mark::hit!(trait_impl); return None; } @@ -87,6 +81,14 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> ) } +pub(crate) fn is_trait_impl(func: &ast::Fn) -> bool { + func.syntax() + .parent() // AssocItemList + .and_then(|x| x.parent()) + .and_then(ast::Impl::cast) + .map_or(false, |imp| imp.trait_().is_some()) +} + fn process_usages( ctx: &AssistContext<'_>, builder: &mut SourceChangeBuilder, diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 2ebb5ef9b19..30a0ce40e30 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -114,6 +114,7 @@ mod handlers { mod add_turbo_fish; mod apply_demorgan; mod auto_import; + mod bind_unused_param; mod change_visibility; mod convert_bool_then; mod convert_comment_block; @@ -224,6 +225,7 @@ mod handlers { add_turbo_fish::add_turbo_fish, apply_demorgan::apply_demorgan, auto_import::auto_import, + bind_unused_param::bind_unused_param, change_visibility::change_visibility, convert_bool_then::convert_bool_then_to_if, convert_bool_then::convert_if_to_bool_then, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 6eadc3dbcbc..0a30d6537dd 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -265,6 +265,21 @@ pub mod std { pub mod collections { pub struct HashMap { } } } ) } +#[test] +fn doctest_bind_unused_param() { + check_doc_test( + "bind_unused_param", + r#####" +fn some_function(x: i32$0) {} +"#####, + r#####" +fn some_function(x: i32) { + let _ = x; +} +"#####, + ) +} + #[test] fn doctest_change_visibility() { check_doc_test( From 19e99941b640db1f4ea40952df925d00902ab8cf Mon Sep 17 00:00:00 2001 From: vsrs Date: Mon, 28 Aug 2023 15:30:44 +0700 Subject: [PATCH 038/250] Add cov_mark tests --- .../src/handlers/bind_unused_param.rs | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/crates/ide-assists/src/handlers/bind_unused_param.rs b/crates/ide-assists/src/handlers/bind_unused_param.rs index 0a22d26193a..7d8672dbbd9 100644 --- a/crates/ide-assists/src/handlers/bind_unused_param.rs +++ b/crates/ide-assists/src/handlers/bind_unused_param.rs @@ -60,6 +60,7 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let right_line = line_index.line_col(r_curly_range.start()).line; if left_line == right_line { + cov_mark::hit!(single_line); text.push('\n'); } @@ -75,12 +76,13 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O #[cfg(test)] mod tests { - use crate::tests::check_assist; + use crate::tests::{check_assist, check_assist_not_applicable}; use super::*; #[test] fn bind_unused_empty_block() { + cov_mark::check!(single_line); check_assist( bind_unused_param, r#" @@ -124,6 +126,33 @@ fn foo(y: T) where T : Default { let _ = y; } +"#, + ); + } + + #[test] + fn trait_impl() { + cov_mark::check!(trait_impl); + check_assist_not_applicable( + bind_unused_param, + r#" +trait Trait { + fn foo(x: i32); +} +impl Trait for () { + fn foo($0x: i32) {} +} +"#, + ); + } + + #[test] + fn keep_used() { + cov_mark::check!(keep_used); + check_assist_not_applicable( + bind_unused_param, + r#" +fn foo(x: i32, $0y: i32) { y; } "#, ); } From a671127941921bc11be4d99106fea5cba398b383 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 28 Aug 2023 15:42:23 +0200 Subject: [PATCH 039/250] closure field capturing: don't depend on alignment of packed fields --- compiler/rustc_hir_typeck/src/upvar.rs | 46 ++++--------------- .../2229_closure_analysis/repr_packed.rs | 10 ++-- .../2229_closure_analysis/repr_packed.stderr | 19 +++----- 3 files changed, 22 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 1a41786d251..ba469bd029d 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -195,7 +195,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assert_eq!(self.tcx.hir().body_owner_def_id(body.id()), closure_def_id); let mut delegate = InferBorrowKind { - fcx: self, closure_def_id, capture_information: Default::default(), fake_reads: Default::default(), @@ -1607,34 +1606,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Truncate the capture so that the place being borrowed is in accordance with RFC 1240, /// which states that it's unsafe to take a reference into a struct marked `repr(packed)`. fn restrict_repr_packed_field_ref_capture<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, mut place: Place<'tcx>, mut curr_borrow_kind: ty::UpvarCapture, ) -> (Place<'tcx>, ty::UpvarCapture) { let pos = place.projections.iter().enumerate().position(|(i, p)| { let ty = place.ty_before_projection(i); - // Return true for fields of packed structs, unless those fields have alignment 1. + // Return true for fields of packed structs. match p.kind { ProjectionKind::Field(..) => match ty.kind() { ty::Adt(def, _) if def.repr().packed() => { - // We erase regions here because they cannot be hashed - match tcx.layout_of(param_env.and(tcx.erase_regions(p.ty))) { - Ok(layout) if layout.align.abi.bytes() == 1 => { - // if the alignment is 1, the type can't be further - // disaligned. - debug!( - "restrict_repr_packed_field_ref_capture: ({:?}) - align = 1", - place - ); - false - } - _ => { - debug!("restrict_repr_packed_field_ref_capture: ({:?}) - true", place); - true - } - } + // We stop here regardless of field alignment. Field alignment can change as + // types change, including the types of private fields in other crates, and that + // shouldn't affect how we compute our captures. + true } _ => false, @@ -1689,9 +1674,7 @@ fn drop_location_span(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> Span { tcx.sess.source_map().end_point(owner_span) } -struct InferBorrowKind<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, - +struct InferBorrowKind<'tcx> { // The def-id of the closure whose kind and upvar accesses are being inferred. closure_def_id: LocalDefId, @@ -1725,7 +1708,7 @@ struct InferBorrowKind<'a, 'tcx> { fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>, } -impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { +impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { fn fake_read( &mut self, place: &PlaceWithHirId<'tcx>, @@ -1740,12 +1723,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { let (place, _) = restrict_capture_precision(place.place.clone(), dummy_capture_kind); - let (place, _) = restrict_repr_packed_field_ref_capture( - self.fcx.tcx, - self.fcx.param_env, - place, - dummy_capture_kind, - ); + let (place, _) = restrict_repr_packed_field_ref_capture(place, dummy_capture_kind); self.fake_reads.push((place, cause, diag_expr_id)); } @@ -1780,12 +1758,8 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { // We only want repr packed restriction to be applied to reading references into a packed // struct, and not when the data is being moved. Therefore we call this method here instead // of in `restrict_capture_precision`. - let (place, mut capture_kind) = restrict_repr_packed_field_ref_capture( - self.fcx.tcx, - self.fcx.param_env, - place_with_id.place.clone(), - capture_kind, - ); + let (place, mut capture_kind) = + restrict_repr_packed_field_ref_capture(place_with_id.place.clone(), capture_kind); // Raw pointers don't inherit mutability if place_with_id.place.deref_tys().any(Ty::is_unsafe_ptr) { diff --git a/tests/ui/closures/2229_closure_analysis/repr_packed.rs b/tests/ui/closures/2229_closure_analysis/repr_packed.rs index f23670f63ac..8c23454fae9 100644 --- a/tests/ui/closures/2229_closure_analysis/repr_packed.rs +++ b/tests/ui/closures/2229_closure_analysis/repr_packed.rs @@ -3,7 +3,8 @@ #![feature(rustc_attrs)] // `u8` aligned at a byte and are unaffected by repr(packed). -// Therefore we can precisely (and safely) capture references to both the fields. +// Therefore we *could* precisely (and safely) capture references to both the fields, +// but we don't, since we don't want capturing to change when field types change alignment. fn test_alignment_not_affected() { #[repr(packed)] struct Foo { x: u8, y: u8 } @@ -17,11 +18,10 @@ fn test_alignment_not_affected() { //~^ ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: let z1: &u8 = &foo.x; - //~^ NOTE: Capturing foo[(0, 0)] -> ImmBorrow - //~| NOTE: Min Capture foo[(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing foo[] -> ImmBorrow let z2: &mut u8 = &mut foo.y; - //~^ NOTE: Capturing foo[(1, 0)] -> MutBorrow - //~| NOTE: Min Capture foo[(1, 0)] -> MutBorrow + //~^ NOTE: Capturing foo[] -> MutBorrow + //~| NOTE: Min Capture foo[] -> MutBorrow *z2 = 42; diff --git a/tests/ui/closures/2229_closure_analysis/repr_packed.stderr b/tests/ui/closures/2229_closure_analysis/repr_packed.stderr index 580061ebc6e..32b3d844c6e 100644 --- a/tests/ui/closures/2229_closure_analysis/repr_packed.stderr +++ b/tests/ui/closures/2229_closure_analysis/repr_packed.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/repr_packed.rs:13:17 + --> $DIR/repr_packed.rs:14:17 | LL | let mut c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error: First Pass analysis includes: - --> $DIR/repr_packed.rs:16:5 + --> $DIR/repr_packed.rs:17:5 | LL | / || { LL | | @@ -37,19 +37,19 @@ LL | | println!("({}, {})", z1, z2); LL | | }; | |_____^ | -note: Capturing foo[(0, 0)] -> ImmBorrow - --> $DIR/repr_packed.rs:19:24 +note: Capturing foo[] -> ImmBorrow + --> $DIR/repr_packed.rs:20:24 | LL | let z1: &u8 = &foo.x; | ^^^^^ -note: Capturing foo[(1, 0)] -> MutBorrow +note: Capturing foo[] -> MutBorrow --> $DIR/repr_packed.rs:22:32 | LL | let z2: &mut u8 = &mut foo.y; | ^^^^^ error: Min Capture analysis includes: - --> $DIR/repr_packed.rs:16:5 + --> $DIR/repr_packed.rs:17:5 | LL | / || { LL | | @@ -60,12 +60,7 @@ LL | | println!("({}, {})", z1, z2); LL | | }; | |_____^ | -note: Min Capture foo[(0, 0)] -> ImmBorrow - --> $DIR/repr_packed.rs:19:24 - | -LL | let z1: &u8 = &foo.x; - | ^^^^^ -note: Min Capture foo[(1, 0)] -> MutBorrow +note: Min Capture foo[] -> MutBorrow --> $DIR/repr_packed.rs:22:32 | LL | let z2: &mut u8 = &mut foo.y; From 514fefab9c3c600dea185157cd8337f3548e3684 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Mon, 28 Aug 2023 22:22:28 +0330 Subject: [PATCH 040/250] Respect `#[allow(unused_braces)]` --- .../src/handlers/mutability_errors.rs | 2 +- .../src/handlers/useless_braces.rs | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index e0c3bedce46..8976a3de67b 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -76,7 +76,7 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Di "variable does not need to be mutable", ast, ) - .experimental() // Not supporting `#[allow(unused_mut)]` leads to false positive. + .experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive. .with_fixes(fixes) } diff --git a/crates/ide-diagnostics/src/handlers/useless_braces.rs b/crates/ide-diagnostics/src/handlers/useless_braces.rs index 0aa439f797a..c4ac59ec2a4 100644 --- a/crates/ide-diagnostics/src/handlers/useless_braces.rs +++ b/crates/ide-diagnostics/src/handlers/useless_braces.rs @@ -1,3 +1,4 @@ +use hir::InFile; use ide_db::{base_db::FileId, source_change::SourceChange}; use itertools::Itertools; use syntax::{ast, AstNode, SyntaxNode}; @@ -39,6 +40,7 @@ pub(crate) fn useless_braces( "Unnecessary braces in use statement".to_string(), use_range, ) + .with_main_node(InFile::new(file_id.into(), node.clone())) .with_fixes(Some(vec![fix( "remove_braces", "Remove unnecessary braces", @@ -153,6 +155,25 @@ use a::{c, d::{e$0}}; r#" mod a { pub mod c {} pub mod d { pub mod e {} } } use a::{c, d::e}; +"#, + ); + } + + #[test] + fn respect_lint_attributes_for_unused_braces() { + check_diagnostics( + r#" +mod b {} +#[allow(unused_braces)] +use {b}; +"#, + ); + check_diagnostics( + r#" +mod b {} +#[deny(unused_braces)] +use {b}; + //^^^ 💡 error: Unnecessary braces in use statement "#, ); } From 3431fb7b61f028bc0d50565d1ea90d83af1e2b40 Mon Sep 17 00:00:00 2001 From: Meng Xiangzhuo Date: Tue, 29 Aug 2023 05:37:25 +0800 Subject: [PATCH 041/250] fix a doc typo at std::os::unix::fs::FileExt::write_at --- library/std/src/os/unix/fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 029de8fbf76..f8168c927c4 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -155,7 +155,7 @@ pub trait FileExt { /// flag fail to respect the offset parameter, always appending to the end /// of the file instead. /// - /// It is possible to inadvertantly set this flag, like in the example below. + /// It is possible to inadvertently set this flag, like in the example below. /// Therefore, it is important to be vigilant while changing options to mitigate /// unexpected behaviour. /// From 57fccf9e5b88407272e90ae4df5b652cbdfb4cc4 Mon Sep 17 00:00:00 2001 From: Meng Xiangzhuo Date: Tue, 29 Aug 2023 05:57:58 +0800 Subject: [PATCH 042/250] fix std::primitive doc: homogenous -> homogeneous --- library/core/src/primitive_docs.rs | 6 +++--- library/std/src/primitive_docs.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 80289ca08c3..2cfa896e5b9 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -612,7 +612,7 @@ mod prim_pointer {} /// statically generated up to size 32. /// /// Arrays of sizes from 1 to 12 (inclusive) implement [`From`], where `Tuple` -/// is a homogenous [prim@tuple] of appropriate length. +/// is a homogeneous [prim@tuple] of appropriate length. /// /// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on /// an array. Indeed, this provides most of the API for working with arrays. @@ -676,7 +676,7 @@ mod prim_pointer {} /// move_away(roa); /// ``` /// -/// Arrays can be created from homogenous tuples of appropriate length: +/// Arrays can be created from homogeneous tuples of appropriate length: /// /// ``` /// let tuple: (u32, u32, u32) = (1, 2, 3); @@ -1065,7 +1065,7 @@ mod prim_str {} /// assert_eq!(y, 5); /// ``` /// -/// Homogenous tuples can be created from arrays of appropriate length: +/// Homogeneous tuples can be created from arrays of appropriate length: /// /// ``` /// let array: [u32; 3] = [1, 2, 3]; diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index 80289ca08c3..2cfa896e5b9 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -612,7 +612,7 @@ mod prim_pointer {} /// statically generated up to size 32. /// /// Arrays of sizes from 1 to 12 (inclusive) implement [`From`], where `Tuple` -/// is a homogenous [prim@tuple] of appropriate length. +/// is a homogeneous [prim@tuple] of appropriate length. /// /// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on /// an array. Indeed, this provides most of the API for working with arrays. @@ -676,7 +676,7 @@ mod prim_pointer {} /// move_away(roa); /// ``` /// -/// Arrays can be created from homogenous tuples of appropriate length: +/// Arrays can be created from homogeneous tuples of appropriate length: /// /// ``` /// let tuple: (u32, u32, u32) = (1, 2, 3); @@ -1065,7 +1065,7 @@ mod prim_str {} /// assert_eq!(y, 5); /// ``` /// -/// Homogenous tuples can be created from arrays of appropriate length: +/// Homogeneous tuples can be created from arrays of appropriate length: /// /// ``` /// let array: [u32; 3] = [1, 2, 3]; From 6b20c1b09193603e9f76b2e73a5f04a982d38b4b Mon Sep 17 00:00:00 2001 From: vsrs Date: Tue, 29 Aug 2023 13:39:56 +0700 Subject: [PATCH 043/250] Apply suggestions. --- .../src/handlers/bind_unused_param.rs | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/crates/ide-assists/src/handlers/bind_unused_param.rs b/crates/ide-assists/src/handlers/bind_unused_param.rs index 7d8672dbbd9..ef88592ba37 100644 --- a/crates/ide-assists/src/handlers/bind_unused_param.rs +++ b/crates/ide-assists/src/handlers/bind_unused_param.rs @@ -27,10 +27,7 @@ use super::remove_unused_param::is_trait_impl; pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let param: ast::Param = ctx.find_node_at_offset()?; - let ident_pat = match param.pat()? { - ast::Pat::IdentPat(it) => it, - _ => return None, - }; + let Some(ast::Pat::IdentPat(ident_pat)) = param.pat() else { return None }; let func = param.syntax().ancestors().find_map(ast::Fn::cast)?; if is_trait_impl(&func) { @@ -47,28 +44,29 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O return None; } - let line_index = ctx.db().line_index(ctx.file_id()); - - let mut indent = func.indent_level(); - indent.0 += 1; - let mut text = format!("\n{indent}let _ = {ident_pat};"); - let stmt_list = func.body()?.stmt_list()?; let l_curly_range = stmt_list.l_curly_token()?.text_range(); let r_curly_range = stmt_list.r_curly_token()?.text_range(); - let left_line = line_index.line_col(l_curly_range.end()).line; - let right_line = line_index.line_col(r_curly_range.start()).line; - - if left_line == right_line { - cov_mark::hit!(single_line); - text.push('\n'); - } acc.add( - AssistId("bind_unused_param", AssistKind::Refactor), + AssistId("bind_unused_param", AssistKind::QuickFix), &format!("Bind as `let _ = {};`", &ident_pat), param.syntax().text_range(), |builder| { + let line_index = ctx.db().line_index(ctx.file_id()); + + let mut indent = func.indent_level(); + indent.0 += 1; + let mut text = format!("\n{indent}let _ = {ident_pat};"); + + let left_line = line_index.line_col(l_curly_range.end()).line; + let right_line = line_index.line_col(r_curly_range.start()).line; + + if left_line == right_line { + cov_mark::hit!(single_line); + text.push('\n'); + } + builder.insert(l_curly_range.end(), text); }, ) From 62d189702c7a14bfd17384f8000a0dcb1ce7fe6b Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 29 Aug 2023 13:04:04 +0300 Subject: [PATCH 044/250] Only send inlay hint refresh requests on initial load Editor itself is able to invalidate hints after edits, and /refresh was sent after editor reports changes to the language server. This forces the editor to either query & invalidate the hints twice after every edit, or wait for /refresh to come before querying the hints. Both options are rather useless, so instead, send a request on server startup only: client editors do not know when the server actually starts up, this will help to query the initial hints after editor was open and the server was still starting up. --- crates/rust-analyzer/src/global_state.rs | 2 ++ crates/rust-analyzer/src/main_loop.rs | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index ea8a6975195..3f40397718c 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -66,6 +66,7 @@ pub(crate) struct GlobalState { // status pub(crate) shutdown_requested: bool, + pub(crate) send_hint_refresh_query: bool, pub(crate) last_reported_status: Option, // proc macros @@ -177,6 +178,7 @@ impl GlobalState { mem_docs: MemDocs::default(), semantic_tokens_cache: Arc::new(Default::default()), shutdown_requested: false, + send_hint_refresh_query: false, last_reported_status: None, source_root_config: SourceRootConfig::default(), config_errors: Default::default(), diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 74036710fa3..34b6350ca44 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -317,8 +317,11 @@ impl GlobalState { } // Refresh inlay hints if the client supports it. - if self.config.inlay_hints_refresh() { + if (self.send_hint_refresh_query || self.proc_macro_changed) + && self.config.inlay_hints_refresh() + { self.send_request::((), |_, _| ()); + self.send_hint_refresh_query = false; } } @@ -509,6 +512,7 @@ impl GlobalState { } self.switch_workspaces("fetched build data".to_string()); + self.send_hint_refresh_query = true; (Some(Progress::End), None) } @@ -525,7 +529,7 @@ impl GlobalState { ProcMacroProgress::End(proc_macro_load_result) => { self.fetch_proc_macros_queue.op_completed(true); self.set_proc_macros(proc_macro_load_result); - + self.send_hint_refresh_query = true; (Some(Progress::End), None) } }; From 9dd682803fbf1528034ad0e254b29a8540205e02 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Aug 2023 08:58:38 +0200 Subject: [PATCH 045/250] repr(transparent): it's fine if the one non-1-ZST field is a ZST --- compiler/rustc_hir_analysis/messages.ftl | 12 +-- .../rustc_hir_analysis/src/check/check.rs | 58 +++++-------- tests/ui/repr/repr-transparent.rs | 23 +++--- tests/ui/repr/repr-transparent.stderr | 82 +++++++++++-------- 4 files changed, 88 insertions(+), 87 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 597cae6ff33..c99913ae049 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -270,13 +270,13 @@ hir_analysis_transparent_enum_variant = transparent enum needs exactly one varia .many_label = too many variants in `{$path}` .multi_label = variant here -hir_analysis_transparent_non_zero_sized = transparent {$desc} needs at most one non-zero-sized field, but has {$field_count} - .label = needs at most one non-zero-sized field, but has {$field_count} - .labels = this field is non-zero-sized +hir_analysis_transparent_non_zero_sized = transparent {$desc} needs at most one field with non-trivial size or alignment, but has {$field_count} + .label = needs at most one field with non-trivial size or alignment, but has {$field_count} + .labels = this field has non-zero size or requires alignment -hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$desc} needs at most one non-zero-sized field, but has {$field_count} - .label = needs at most one non-zero-sized field, but has {$field_count} - .labels = this field is non-zero-sized +hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$desc} needs at most one field with non-trivial size or alignment, but has {$field_count} + .label = needs at most one field with non-trivial size or alignment, but has {$field_count} + .labels = this field has non-zero size or requires alignment hir_analysis_typeof_reserved_keyword_used = `typeof` is a reserved keyword but unimplemented diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 46e8cf81bc1..3c00a246a3d 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1130,19 +1130,19 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) return; } - // For each field, figure out if it's known to be a ZST and align(1), with "known" - // respecting #[non_exhaustive] attributes. + // For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with + // "known" respecting #[non_exhaustive] attributes. let field_infos = adt.all_fields().map(|field| { let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did)); let param_env = tcx.param_env(field.did); let layout = tcx.layout_of(param_env.and(ty)); // We are currently checking the type this field came from, so it must be local let span = tcx.hir().span_if_local(field.did).unwrap(); - let zst = layout.is_ok_and(|layout| layout.is_zst()); - let align = layout.ok().map(|layout| layout.align.abi.bytes()); - if !zst { - return (span, zst, align, None); + let trivial = layout.is_ok_and(|layout| layout.is_1zst()); + if !trivial { + return (span, trivial, None); } + // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`. fn check_non_exhaustive<'tcx>( tcx: TyCtxt<'tcx>, @@ -1176,41 +1176,25 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } } - (span, zst, align, check_non_exhaustive(tcx, ty).break_value()) + (span, trivial, check_non_exhaustive(tcx, ty).break_value()) }); - let non_zst_fields = field_infos + let non_trivial_fields = field_infos .clone() - .filter_map(|(span, zst, _align, _non_exhaustive)| if !zst { Some(span) } else { None }); - let non_zst_count = non_zst_fields.clone().count(); - if non_zst_count >= 2 { - bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, tcx.def_span(adt.did())); + .filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None }); + let non_trivial_count = non_trivial_fields.clone().count(); + if non_trivial_count >= 2 { + bad_non_zero_sized_fields( + tcx, + adt, + non_trivial_count, + non_trivial_fields, + tcx.def_span(adt.did()), + ); + return; } - let incompatible_zst_fields = - field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count(); - let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2; - for (span, zst, align, non_exhaustive) in field_infos { - if zst && align != Some(1) { - let mut err = struct_span_err!( - tcx.sess, - span, - E0691, - "zero-sized field in transparent {} has alignment larger than 1", - adt.descr(), - ); - - if let Some(align_bytes) = align { - err.span_label( - span, - format!("has alignment of {align_bytes}, which is larger than 1"), - ); - } else { - err.span_label(span, "may have alignment larger than 1"); - } - - err.emit(); - } - if incompat && let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive { + for (span, _trivial, non_exhaustive) in field_infos { + if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive { tcx.struct_span_lint_hir( REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()), diff --git a/tests/ui/repr/repr-transparent.rs b/tests/ui/repr/repr-transparent.rs index 8c9d1639c0a..87cf59ce9af 100644 --- a/tests/ui/repr/repr-transparent.rs +++ b/tests/ui/repr/repr-transparent.rs @@ -23,23 +23,26 @@ struct ContainsMultipleZst(PhantomData<*const i32>, NoFields); struct ContainsZstAndNonZst((), [i32; 2]); #[repr(transparent)] -struct MultipleNonZst(u8, u8); //~ ERROR needs at most one non-zero-sized field +struct MultipleNonZst(u8, u8); //~ ERROR needs at most one field with non-trivial size or alignment trait Mirror { type It: ?Sized; } impl Mirror for T { type It = Self; } #[repr(transparent)] pub struct StructWithProjection(f32, ::It); -//~^ ERROR needs at most one non-zero-sized field +//~^ ERROR needs at most one field with non-trivial size or alignment #[repr(transparent)] -struct NontrivialAlignZst(u32, [u16; 0]); //~ ERROR alignment larger than 1 +struct NontrivialAlignZst(u32, [u16; 0]); //~ ERROR needs at most one field with non-trivial size or alignment #[repr(align(32))] struct ZstAlign32(PhantomData); #[repr(transparent)] -struct GenericAlign(ZstAlign32, u32); //~ ERROR alignment larger than 1 +struct GenericAlign(ZstAlign32, u32); //~ ERROR needs at most one field with non-trivial size or alignment + +#[repr(transparent)] +struct WrapsZstWithAlignment([i32; 0]); #[repr(transparent)] //~ ERROR unsupported representation for zero-variant enum enum Void {} //~ ERROR transparent enum needs exactly one variant, but has 0 @@ -58,7 +61,7 @@ enum UnitFieldEnum { enum TooManyFieldsEnum { Foo(u32, String), } -//~^^^ ERROR transparent enum needs at most one non-zero-sized field, but has 2 +//~^^^ ERROR transparent enum needs at most one field with non-trivial size or alignment, but has 2 #[repr(transparent)] enum MultipleVariants { //~ ERROR transparent enum needs exactly one variant, but has 2 @@ -67,13 +70,13 @@ enum MultipleVariants { //~ ERROR transparent enum needs exactly one variant, bu } #[repr(transparent)] -enum NontrivialAlignZstEnum { - Foo(u32, [u16; 0]), //~ ERROR alignment larger than 1 +enum NontrivialAlignZstEnum { //~ ERROR needs at most one field with non-trivial size or alignment + Foo(u32, [u16; 0]), } #[repr(transparent)] -enum GenericAlignEnum { - Foo { bar: ZstAlign32, baz: u32 } //~ ERROR alignment larger than 1 +enum GenericAlignEnum { //~ ERROR needs at most one field with non-trivial size or alignment + Foo { bar: ZstAlign32, baz: u32 } } #[repr(transparent)] @@ -82,7 +85,7 @@ union UnitUnion { } #[repr(transparent)] -union TooManyFields { //~ ERROR transparent union needs at most one non-zero-sized field, but has 2 +union TooManyFields { //~ ERROR transparent union needs at most one field with non-trivial size or alignment, but has 2 u: u32, s: i32 } diff --git a/tests/ui/repr/repr-transparent.stderr b/tests/ui/repr/repr-transparent.stderr index 028fc25db46..d0c78a8418a 100644 --- a/tests/ui/repr/repr-transparent.stderr +++ b/tests/ui/repr/repr-transparent.stderr @@ -1,35 +1,41 @@ -error[E0690]: transparent struct needs at most one non-zero-sized field, but has 2 +error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2 --> $DIR/repr-transparent.rs:26:1 | LL | struct MultipleNonZst(u8, u8); - | ^^^^^^^^^^^^^^^^^^^^^ -- -- this field is non-zero-sized + | ^^^^^^^^^^^^^^^^^^^^^ -- -- this field has non-zero size or requires alignment | | | - | | this field is non-zero-sized - | needs at most one non-zero-sized field, but has 2 + | | this field has non-zero size or requires alignment + | needs at most one field with non-trivial size or alignment, but has 2 -error[E0690]: transparent struct needs at most one non-zero-sized field, but has 2 +error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2 --> $DIR/repr-transparent.rs:32:1 | LL | pub struct StructWithProjection(f32, ::It); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- ------------------- this field is non-zero-sized + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --- ------------------- this field has non-zero size or requires alignment | | | - | | this field is non-zero-sized - | needs at most one non-zero-sized field, but has 2 + | | this field has non-zero size or requires alignment + | needs at most one field with non-trivial size or alignment, but has 2 -error[E0691]: zero-sized field in transparent struct has alignment larger than 1 - --> $DIR/repr-transparent.rs:36:32 +error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2 + --> $DIR/repr-transparent.rs:36:1 | LL | struct NontrivialAlignZst(u32, [u16; 0]); - | ^^^^^^^^ has alignment of 2, which is larger than 1 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ --- -------- this field has non-zero size or requires alignment + | | | + | | this field has non-zero size or requires alignment + | needs at most one field with non-trivial size or alignment, but has 2 -error[E0691]: zero-sized field in transparent struct has alignment larger than 1 - --> $DIR/repr-transparent.rs:42:24 +error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2 + --> $DIR/repr-transparent.rs:42:1 | LL | struct GenericAlign(ZstAlign32, u32); - | ^^^^^^^^^^^^^ has alignment of 32, which is larger than 1 + | ^^^^^^^^^^^^^^^^^^^^^^ ------------- --- this field has non-zero size or requires alignment + | | | + | | this field has non-zero size or requires alignment + | needs at most one field with non-trivial size or alignment, but has 2 error[E0084]: unsupported representation for zero-variant enum - --> $DIR/repr-transparent.rs:44:1 + --> $DIR/repr-transparent.rs:47:1 | LL | #[repr(transparent)] | ^^^^^^^^^^^^^^^^^^^^ @@ -37,23 +43,23 @@ LL | enum Void {} | --------- zero-variant enum error[E0731]: transparent enum needs exactly one variant, but has 0 - --> $DIR/repr-transparent.rs:45:1 + --> $DIR/repr-transparent.rs:48:1 | LL | enum Void {} | ^^^^^^^^^ needs exactly one variant, but has 0 -error[E0690]: the variant of a transparent enum needs at most one non-zero-sized field, but has 2 - --> $DIR/repr-transparent.rs:58:1 +error[E0690]: the variant of a transparent enum needs at most one field with non-trivial size or alignment, but has 2 + --> $DIR/repr-transparent.rs:61:1 | LL | enum TooManyFieldsEnum { - | ^^^^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2 + | ^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2 LL | Foo(u32, String), - | --- ------ this field is non-zero-sized + | --- ------ this field has non-zero size or requires alignment | | - | this field is non-zero-sized + | this field has non-zero size or requires alignment error[E0731]: transparent enum needs exactly one variant, but has 2 - --> $DIR/repr-transparent.rs:64:1 + --> $DIR/repr-transparent.rs:67:1 | LL | enum MultipleVariants { | ^^^^^^^^^^^^^^^^^^^^^ needs exactly one variant, but has 2 @@ -62,29 +68,37 @@ LL | Foo(String), LL | Bar, | --- too many variants in `MultipleVariants` -error[E0691]: zero-sized field in transparent enum has alignment larger than 1 - --> $DIR/repr-transparent.rs:71:14 +error[E0690]: the variant of a transparent enum needs at most one field with non-trivial size or alignment, but has 2 + --> $DIR/repr-transparent.rs:73:1 | +LL | enum NontrivialAlignZstEnum { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2 LL | Foo(u32, [u16; 0]), - | ^^^^^^^^ has alignment of 2, which is larger than 1 + | --- -------- this field has non-zero size or requires alignment + | | + | this field has non-zero size or requires alignment -error[E0691]: zero-sized field in transparent enum has alignment larger than 1 - --> $DIR/repr-transparent.rs:76:11 +error[E0690]: the variant of a transparent enum needs at most one field with non-trivial size or alignment, but has 2 + --> $DIR/repr-transparent.rs:78:1 | +LL | enum GenericAlignEnum { + | ^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2 LL | Foo { bar: ZstAlign32, baz: u32 } - | ^^^^^^^^^^^^^^^^^^ has alignment of 32, which is larger than 1 + | ------------------ -------- this field has non-zero size or requires alignment + | | + | this field has non-zero size or requires alignment -error[E0690]: transparent union needs at most one non-zero-sized field, but has 2 - --> $DIR/repr-transparent.rs:85:1 +error[E0690]: transparent union needs at most one field with non-trivial size or alignment, but has 2 + --> $DIR/repr-transparent.rs:88:1 | LL | union TooManyFields { - | ^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2 + | ^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2 LL | u: u32, - | ------ this field is non-zero-sized + | ------ this field has non-zero size or requires alignment LL | s: i32 - | ------ this field is non-zero-sized + | ------ this field has non-zero size or requires alignment error: aborting due to 11 previous errors -Some errors have detailed explanations: E0084, E0690, E0691, E0731. +Some errors have detailed explanations: E0084, E0690, E0731. For more information about an error, try `rustc --explain E0084`. From a6ccd265e6b0cad3107187fccbfee24d08158385 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Aug 2023 19:07:17 +0200 Subject: [PATCH 046/250] mark error code as removed --- compiler/rustc_error_codes/src/error_codes/E0691.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0691.md b/compiler/rustc_error_codes/src/error_codes/E0691.md index 483c74c0ff5..a5bedd61e92 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0691.md +++ b/compiler/rustc_error_codes/src/error_codes/E0691.md @@ -1,9 +1,11 @@ +#### Note: this error code is no longer emitted by the compiler. + A struct, enum, or union with the `repr(transparent)` representation hint contains a zero-sized field that requires non-trivial alignment. Erroneous code example: -```compile_fail,E0691 +```ignore (error is no longer emitted) #![feature(repr_align)] #[repr(align(32))] From 6b559c4a9aabeb151678479265e4d82018fedaeb Mon Sep 17 00:00:00 2001 From: vsrs Date: Tue, 29 Aug 2023 22:56:31 +0700 Subject: [PATCH 047/250] Better trait implementation support --- .../src/handlers/bind_unused_param.rs | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/crates/ide-assists/src/handlers/bind_unused_param.rs b/crates/ide-assists/src/handlers/bind_unused_param.rs index ef88592ba37..45c1f0ccae3 100644 --- a/crates/ide-assists/src/handlers/bind_unused_param.rs +++ b/crates/ide-assists/src/handlers/bind_unused_param.rs @@ -9,8 +9,6 @@ use syntax::{ AstNode, }; -use super::remove_unused_param::is_trait_impl; - // Assist: bind_unused_param // // Binds unused function parameter to an underscore. @@ -29,12 +27,6 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let Some(ast::Pat::IdentPat(ident_pat)) = param.pat() else { return None }; - let func = param.syntax().ancestors().find_map(ast::Fn::cast)?; - if is_trait_impl(&func) { - cov_mark::hit!(trait_impl); - return None; - } - let param_def = { let local = ctx.sema.to_def(&ident_pat)?; Definition::Local(local) @@ -44,6 +36,7 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O return None; } + let func = param.syntax().ancestors().find_map(ast::Fn::cast)?; let stmt_list = func.body()?.stmt_list()?; let l_curly_range = stmt_list.l_curly_token()?.text_range(); let r_curly_range = stmt_list.r_curly_token()?.text_range(); @@ -55,16 +48,16 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O |builder| { let line_index = ctx.db().line_index(ctx.file_id()); - let mut indent = func.indent_level(); - indent.0 += 1; - let mut text = format!("\n{indent}let _ = {ident_pat};"); + let indent = func.indent_level(); + let text_indent = indent + 1; + let mut text = format!("\n{text_indent}let _ = {ident_pat};"); let left_line = line_index.line_col(l_curly_range.end()).line; let right_line = line_index.line_col(r_curly_range.start()).line; if left_line == right_line { cov_mark::hit!(single_line); - text.push('\n'); + text.push_str(&format!("\n{indent}")); } builder.insert(l_curly_range.end(), text); @@ -130,8 +123,7 @@ where T : Default { #[test] fn trait_impl() { - cov_mark::check!(trait_impl); - check_assist_not_applicable( + check_assist( bind_unused_param, r#" trait Trait { @@ -140,6 +132,16 @@ trait Trait { impl Trait for () { fn foo($0x: i32) {} } +"#, + r#" +trait Trait { + fn foo(x: i32); +} +impl Trait for () { + fn foo(x: i32) { + let _ = x; + } +} "#, ); } From 1eb6d2e9a90da9a5738192b60b073b6b71c7deef Mon Sep 17 00:00:00 2001 From: vsrs Date: Tue, 29 Aug 2023 23:06:12 +0700 Subject: [PATCH 048/250] Rollback changes in remove_unused_param.rs --- .../src/handlers/remove_unused_param.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/ide-assists/src/handlers/remove_unused_param.rs b/crates/ide-assists/src/handlers/remove_unused_param.rs index aa1002ee393..0772b168d49 100644 --- a/crates/ide-assists/src/handlers/remove_unused_param.rs +++ b/crates/ide-assists/src/handlers/remove_unused_param.rs @@ -42,7 +42,13 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> param.syntax().parent()?.children().find_map(ast::SelfParam::cast).is_some(); // check if fn is in impl Trait for .. - if is_trait_impl(&func) { + if func + .syntax() + .parent() // AssocItemList + .and_then(|x| x.parent()) + .and_then(ast::Impl::cast) + .map_or(false, |imp| imp.trait_().is_some()) + { cov_mark::hit!(trait_impl); return None; } @@ -81,14 +87,6 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> ) } -pub(crate) fn is_trait_impl(func: &ast::Fn) -> bool { - func.syntax() - .parent() // AssocItemList - .and_then(|x| x.parent()) - .and_then(ast::Impl::cast) - .map_or(false, |imp| imp.trait_().is_some()) -} - fn process_usages( ctx: &AssistContext<'_>, builder: &mut SourceChangeBuilder, From ea74cc4b9a159087177506fecc1ce900468a0ef1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 30 Aug 2023 19:53:47 +0200 Subject: [PATCH 049/250] Update architecture.md --- docs/dev/architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index 895de5798ac..b7d585cafb3 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md @@ -268,7 +268,7 @@ They are independent from the rest of the code. And it also handles the actual parsing and expansion of declarative macro (a-la "Macros By Example" or mbe). For proc macros, the client-server model are used. -We pass an argument `--proc-macro` to `rust-analyzer` binary to start a separate process (`proc_macro_srv`). +We start a separate process (`proc_macro_srv`) which loads and runs the proc-macros for us. And the client (`proc_macro_api`) provides an interface to talk to that server separately. And then token trees are passed from client, and the server will load the corresponding dynamic library (which built by `cargo`). From 9c0e5ebf65ababd5ccae1211cb4831e605311b9a Mon Sep 17 00:00:00 2001 From: soqb Date: Fri, 1 Sep 2023 12:29:40 +0100 Subject: [PATCH 050/250] fix `Debug` impl for `AsciiChar` --- library/core/src/ascii/ascii_char.rs | 41 ++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index 5378b210e67..cc872a5343d 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -3,7 +3,7 @@ //! suggestions from rustc if you get anything slightly wrong in here, and overall //! helps with clarity as we're also referring to `char` intentionally in here. -use crate::fmt; +use crate::fmt::{self, Write}; use crate::mem::transmute; /// One of the 128 Unicode characters from U+0000 through U+007F, @@ -54,7 +54,7 @@ use crate::mem::transmute; /// [chart]: https://www.unicode.org/charts/PDF/U0000.pdf /// [NIST FIPS 1-2]: https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub1-2-1977.pdf /// [NamesList]: https://www.unicode.org/Public/15.0.0/ucd/NamesList.txt -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[unstable(feature = "ascii_char", issue = "110998")] #[repr(u8)] pub enum AsciiChar { @@ -563,3 +563,40 @@ impl fmt::Display for AsciiChar { ::fmt(self.as_str(), f) } } + +#[unstable(feature = "ascii_char", issue = "110998")] +impl fmt::Debug for AsciiChar { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + #[inline] + fn backslash(a: AsciiChar) -> ([AsciiChar; 4], u8) { + ([AsciiChar::ReverseSolidus, a, AsciiChar::Null, AsciiChar::Null], 2) + } + + let (buf, len) = match self { + AsciiChar::Null => backslash(AsciiChar::Digit0), + AsciiChar::CharacterTabulation => backslash(AsciiChar::SmallT), + AsciiChar::CarriageReturn => backslash(AsciiChar::SmallR), + AsciiChar::LineFeed => backslash(AsciiChar::SmallN), + AsciiChar::ReverseSolidus => backslash(AsciiChar::ReverseSolidus), + AsciiChar::Apostrophe => backslash(AsciiChar::Apostrophe), + _ => { + let byte = self.to_u8(); + if !byte.is_ascii_control() { + ([*self, AsciiChar::Null, AsciiChar::Null, AsciiChar::Null], 1) + } else { + const HEX_DIGITS: [AsciiChar; 16] = *b"0123456789abcdef".as_ascii().unwrap(); + + let hi = HEX_DIGITS[usize::from(byte >> 4)]; + let lo = HEX_DIGITS[usize::from(byte & 0xf)]; + ([AsciiChar::ReverseSolidus, AsciiChar::SmallX, hi, lo], 4) + } + } + }; + + f.write_char('\'')?; + for byte in &buf[..len as usize] { + f.write_str(byte.as_str())?; + } + f.write_char('\'') + } +} From c09f175d599e74f86d59e4ffabbd256356d7d227 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 1 Sep 2023 17:30:59 +0200 Subject: [PATCH 051/250] Less `once_cell` more `std` --- .cargo/config.toml | 2 +- Cargo.lock | 1 - crates/hir-def/src/attr/builtin.rs | 5 +++-- crates/hir-def/src/lower.rs | 3 ++- crates/ide/src/typing.rs | 11 +++++------ crates/intern/Cargo.toml | 1 - crates/intern/src/lib.rs | 6 +++--- 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 24745d1c806..c9ad7803951 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -8,4 +8,4 @@ lint = "clippy --all-targets -- -Aclippy::collapsible_if -Aclippy::needless_pass linker = "rust-lld" [env] -CARGO_WORKSPACE_DIR = { value = "", relative = true } \ No newline at end of file +CARGO_WORKSPACE_DIR = { value = "", relative = true } diff --git a/Cargo.lock b/Cargo.lock index cf364d28de0..f6900f883a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -854,7 +854,6 @@ version = "0.0.0" dependencies = [ "dashmap", "hashbrown 0.12.3", - "once_cell", "rustc-hash", "triomphe", ] diff --git a/crates/hir-def/src/attr/builtin.rs b/crates/hir-def/src/attr/builtin.rs index cead64a3374..565e48ccd33 100644 --- a/crates/hir-def/src/attr/builtin.rs +++ b/crates/hir-def/src/attr/builtin.rs @@ -8,7 +8,8 @@ //! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to //! ease updating. -use once_cell::sync::OnceCell; +use std::sync::OnceLock; + use rustc_hash::FxHashMap; /// Ignored attribute namespaces used by tools. @@ -29,7 +30,7 @@ pub struct AttributeTemplate { } pub fn find_builtin_attr_idx(name: &str) -> Option { - static BUILTIN_LOOKUP_TABLE: OnceCell> = OnceCell::new(); + static BUILTIN_LOOKUP_TABLE: OnceLock> = OnceLock::new(); BUILTIN_LOOKUP_TABLE .get_or_init(|| { INERT_ATTRIBUTES.iter().map(|attr| attr.name).enumerate().map(|(a, b)| (b, a)).collect() diff --git a/crates/hir-def/src/lower.rs b/crates/hir-def/src/lower.rs index e523c229179..52781d98892 100644 --- a/crates/hir-def/src/lower.rs +++ b/crates/hir-def/src/lower.rs @@ -1,10 +1,11 @@ //! Context for lowering paths. +use std::cell::OnceCell; + use hir_expand::{ ast_id_map::{AstIdMap, AstIdNode}, hygiene::Hygiene, AstId, HirFileId, InFile, }; -use once_cell::unsync::OnceCell; use syntax::ast; use triomphe::Arc; diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs index c7e403f6b1a..27dedab13ea 100644 --- a/crates/ide/src/typing.rs +++ b/crates/ide/src/typing.rs @@ -86,17 +86,16 @@ fn on_char_typed_inner( if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) { return None; } - return match char_typed { + let conv = |text_edit: Option| { + Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false }) + }; + match char_typed { '.' => conv(on_dot_typed(&file.tree(), offset)), '=' => conv(on_eq_typed(&file.tree(), offset)), '<' => on_left_angle_typed(&file.tree(), offset), '>' => conv(on_right_angle_typed(&file.tree(), offset)), '{' => conv(on_opening_brace_typed(file, offset)), - _ => return None, - }; - - fn conv(text_edit: Option) -> Option { - Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false }) + _ => None, } } diff --git a/crates/intern/Cargo.toml b/crates/intern/Cargo.toml index 4d56c771960..89b302c796b 100644 --- a/crates/intern/Cargo.toml +++ b/crates/intern/Cargo.toml @@ -16,6 +16,5 @@ doctest = false # We need to freeze the version of the crate, as the raw-api feature is considered unstable dashmap = { version = "=5.4.0", features = ["raw-api"] } hashbrown.workspace = true -once_cell = "1.17.0" rustc-hash = "1.1.0" triomphe.workspace = true diff --git a/crates/intern/src/lib.rs b/crates/intern/src/lib.rs index dabbf3a38b5..2934d26674d 100644 --- a/crates/intern/src/lib.rs +++ b/crates/intern/src/lib.rs @@ -6,11 +6,11 @@ use std::{ fmt::{self, Debug, Display}, hash::{BuildHasherDefault, Hash, Hasher}, ops::Deref, + sync::OnceLock, }; use dashmap::{DashMap, SharedValue}; use hashbrown::{hash_map::RawEntryMut, HashMap}; -use once_cell::sync::OnceCell; use rustc_hash::FxHasher; use triomphe::Arc; @@ -177,12 +177,12 @@ impl Display for Interned { } pub struct InternStorage { - map: OnceCell>, + map: OnceLock>, } impl InternStorage { pub const fn new() -> Self { - Self { map: OnceCell::new() } + Self { map: OnceLock::new() } } } From 70e21dc30b5cbaecad463adb9ec28e32ae5c35c8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 1 Sep 2023 19:17:57 +0200 Subject: [PATCH 052/250] Remove some allocations in borrowck --- crates/hir-ty/src/mir/borrowck.rs | 77 ++++++++++++++++--------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index c70d7f63fd8..c651ddc2cf6 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -42,30 +42,27 @@ pub struct BorrowckResult { fn all_mir_bodies( db: &dyn HirDatabase, def: DefWithBodyId, -) -> Box, MirLowerError>> + '_> { + mut cb: impl FnMut(Arc), +) -> Result<(), MirLowerError> { fn for_closure( db: &dyn HirDatabase, c: ClosureId, - ) -> Box, MirLowerError>> + '_> { + cb: &mut impl FnMut(Arc), + ) -> Result<(), MirLowerError> { match db.mir_body_for_closure(c) { Ok(body) => { - let closures = body.closures.clone(); - Box::new( - iter::once(Ok(body)) - .chain(closures.into_iter().flat_map(|it| for_closure(db, it))), - ) + cb(body.clone()); + body.closures.iter().map(|&it| for_closure(db, it, cb)).collect() } - Err(e) => Box::new(iter::once(Err(e))), + Err(e) => Err(e), } } match db.mir_body(def) { Ok(body) => { - let closures = body.closures.clone(); - Box::new( - iter::once(Ok(body)).chain(closures.into_iter().flat_map(|it| for_closure(db, it))), - ) + cb(body.clone()); + body.closures.iter().map(|&it| for_closure(db, it, &mut cb)).collect() } - Err(e) => Box::new(iter::once(Err(e))), + Err(e) => Err(e), } } @@ -74,17 +71,15 @@ pub fn borrowck_query( def: DefWithBodyId, ) -> Result, MirLowerError> { let _p = profile::span("borrowck_query"); - let r = all_mir_bodies(db, def) - .map(|body| { - let body = body?; - Ok(BorrowckResult { - mutability_of_locals: mutability_of_locals(db, &body), - moved_out_of_ref: moved_out_of_ref(db, &body), - mir_body: body, - }) - }) - .collect::, MirLowerError>>()?; - Ok(r.into()) + let mut res = vec![]; + all_mir_bodies(db, def, |body| { + res.push(BorrowckResult { + mutability_of_locals: mutability_of_locals(db, &body), + moved_out_of_ref: moved_out_of_ref(db, &body), + mir_body: body, + }); + })?; + Ok(res.into()) } fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec { @@ -277,21 +272,35 @@ fn ever_initialized_map( ); return; }; - let targets = match &terminator.kind { - TerminatorKind::Goto { target } => vec![*target], - TerminatorKind::SwitchInt { targets, .. } => targets.all_targets().to_vec(), + let mut process = |target, is_ever_initialized| { + if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized { + result[target].insert(l, is_ever_initialized); + dfs(db, body, target, l, result); + } + }; + match &terminator.kind { + TerminatorKind::Goto { target } => process(*target, is_ever_initialized), + TerminatorKind::SwitchInt { targets, .. } => { + targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized)); + } TerminatorKind::UnwindResume | TerminatorKind::Abort | TerminatorKind::Return - | TerminatorKind::Unreachable => vec![], + | TerminatorKind::Unreachable => (), TerminatorKind::Call { target, cleanup, destination, .. } => { if destination.projection.len() == 0 && destination.local == l { is_ever_initialized = true; } - target.into_iter().chain(cleanup.into_iter()).copied().collect() + target + .into_iter() + .chain(cleanup.into_iter()) + .for_each(|&it| process(it, is_ever_initialized)); } TerminatorKind::Drop { target, unwind, place: _ } => { - Some(target).into_iter().chain(unwind.into_iter()).copied().collect() + iter::once(target) + .into_iter() + .chain(unwind.into_iter()) + .for_each(|&it| process(it, is_ever_initialized)); } TerminatorKind::DropAndReplace { .. } | TerminatorKind::Assert { .. } @@ -300,13 +309,7 @@ fn ever_initialized_map( | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } => { never!("We don't emit these MIR terminators yet"); - vec![] - } - }; - for target in targets { - if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized { - result[target].insert(l, is_ever_initialized); - dfs(db, body, target, l, result); + () } } } From cc8b78601d3a40bcc8ccf0b6d5efff386d602c1f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 1 Sep 2023 20:45:46 +0200 Subject: [PATCH 053/250] Shuffle some locking around --- crates/rust-analyzer/src/global_state.rs | 30 ++++++++++++++----- .../src/handlers/notification.rs | 11 +++---- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index ea8a6975195..5024e04ffd3 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -12,12 +12,12 @@ use ide_db::base_db::{CrateId, FileLoader, ProcMacroPaths, SourceDatabase}; use load_cargo::SourceRootConfig; use lsp_types::{SemanticTokens, Url}; use nohash_hasher::IntMap; -use parking_lot::{Mutex, RwLock}; +use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard}; use proc_macro_api::ProcMacroServer; use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts}; use rustc_hash::{FxHashMap, FxHashSet}; use triomphe::Arc; -use vfs::AnchoredPathBuf; +use vfs::{AnchoredPathBuf, Vfs}; use crate::{ config::{Config, ConfigError}, @@ -216,12 +216,15 @@ impl GlobalState { let mut file_changes = FxHashMap::default(); let (change, changed_files, workspace_structure_change) = { let mut change = Change::new(); - let (vfs, line_endings_map) = &mut *self.vfs.write(); - let changed_files = vfs.take_changes(); + let mut guard = self.vfs.write(); + let changed_files = guard.0.take_changes(); if changed_files.is_empty() { return false; } + // downgrade to read lock to allow more readers while we are normalizing text + let guard = RwLockWriteGuard::downgrade_to_upgradable(guard); + let vfs: &Vfs = &guard.0; // We need to fix up the changed events a bit. If we have a create or modify for a file // id that is followed by a delete we actually skip observing the file text from the // earlier event, to avoid problems later on. @@ -272,6 +275,7 @@ impl GlobalState { let mut workspace_structure_change = None; // A file was added or deleted let mut has_structure_changes = false; + let mut bytes = vec![]; for file in &changed_files { let vfs_path = &vfs.file_path(file.file_id); if let Some(path) = vfs_path.as_path() { @@ -293,16 +297,28 @@ impl GlobalState { let text = if file.exists() { let bytes = vfs.file_contents(file.file_id).to_vec(); + String::from_utf8(bytes).ok().and_then(|text| { + // FIXME: Consider doing normalization in the `vfs` instead? That allows + // getting rid of some locking let (text, line_endings) = LineEndings::normalize(text); - line_endings_map.insert(file.file_id, line_endings); - Some(Arc::from(text)) + Some((Arc::from(text), line_endings)) }) } else { None }; - change.change_file(file.file_id, text); + // delay `line_endings_map` changes until we are done normalizing the text + // this allows delaying the re-acquisition of the write lock + bytes.push((file.file_id, text)); } + let (vfs, line_endings_map) = &mut *RwLockUpgradableReadGuard::upgrade(guard); + bytes.into_iter().for_each(|(file_id, text)| match text { + None => change.change_file(file_id, None), + Some((text, line_endings)) => { + line_endings_map.insert(file_id, line_endings); + change.change_file(file_id, Some(text)); + } + }); if has_structure_changes { let roots = self.source_root_config.partition(vfs); change.set_roots(roots); diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index e830e5e9a64..3fd10274e0d 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -84,15 +84,16 @@ pub(crate) fn handle_did_change_text_document( } }; - let vfs = &mut state.vfs.write().0; - let file_id = vfs.file_id(&path).unwrap(); let text = apply_document_changes( state.config.position_encoding(), - || std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(), + || { + let vfs = &state.vfs.read().0; + let file_id = vfs.file_id(&path).unwrap(); + std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into() + }, params.content_changes, ); - - vfs.set_file_contents(path, Some(text.into_bytes())); + state.vfs.write().0.set_file_contents(path, Some(text.into_bytes())); } Ok(()) } From 0f1cde709a2010e94032b0119a6657ac527f7dcf Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 29 Aug 2023 23:41:57 +0300 Subject: [PATCH 054/250] On type format '(', by adding closing ')' automatically --- crates/ide/src/typing.rs | 237 ++++++++++++++++++++++++++++--- crates/rust-analyzer/src/caps.rs | 2 +- 2 files changed, 222 insertions(+), 17 deletions(-) diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs index 27dedab13ea..b40509715ba 100644 --- a/crates/ide/src/typing.rs +++ b/crates/ide/src/typing.rs @@ -32,7 +32,7 @@ use crate::SourceChange; pub(crate) use on_enter::on_enter; // Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`. -pub(crate) const TRIGGER_CHARS: &str = ".=<>{"; +pub(crate) const TRIGGER_CHARS: &str = ".=<>{("; struct ExtendedTextEdit { edit: TextEdit, @@ -94,36 +94,49 @@ fn on_char_typed_inner( '=' => conv(on_eq_typed(&file.tree(), offset)), '<' => on_left_angle_typed(&file.tree(), offset), '>' => conv(on_right_angle_typed(&file.tree(), offset)), - '{' => conv(on_opening_brace_typed(file, offset)), + '{' => conv(on_opening_bracket_typed(file, offset, '{')), + '(' => conv(on_opening_bracket_typed(file, offset, '(')), _ => None, } } -/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a -/// block, or a part of a `use` item. -fn on_opening_brace_typed(file: &Parse, offset: TextSize) -> Option { - if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) { +/// Inserts a closing bracket when the user types an opening bracket, wrapping an existing expression in a +/// block, or a part of a `use` item (for `{`). +fn on_opening_bracket_typed( + file: &Parse, + offset: TextSize, + opening_bracket: char, +) -> Option { + let (closing_bracket, expected_ast_bracket) = match opening_bracket { + '{' => ('}', SyntaxKind::L_CURLY), + '(' => (')', SyntaxKind::L_PAREN), + _ => return None, + }; + + if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some(opening_bracket)) { return None; } let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?; - if brace_token.kind() != SyntaxKind::L_CURLY { + if brace_token.kind() != expected_ast_bracket { return None; } - // Remove the `{` to get a better parse tree, and reparse. + // Remove the opening bracket to get a better parse tree, and reparse. let range = brace_token.text_range(); - if !stdx::always!(range.len() == TextSize::of('{')) { + if !stdx::always!(range.len() == TextSize::of(opening_bracket)) { return None; } let file = file.reparse(&Indel::delete(range)); - if let Some(edit) = brace_expr(&file.tree(), offset) { + if let Some(edit) = bracket_expr(&file.tree(), offset, opening_bracket, closing_bracket) { return Some(edit); } - if let Some(edit) = brace_use_path(&file.tree(), offset) { - return Some(edit); + if closing_bracket == '}' { + if let Some(edit) = brace_use_path(&file.tree(), offset) { + return Some(edit); + } } return None; @@ -142,7 +155,12 @@ fn on_opening_brace_typed(file: &Parse, offset: TextSize) -> Option< )) } - fn brace_expr(file: &SourceFile, offset: TextSize) -> Option { + fn bracket_expr( + file: &SourceFile, + offset: TextSize, + opening_bracket: char, + closing_bracket: char, + ) -> Option { let mut expr: ast::Expr = find_node_at_offset(file.syntax(), offset)?; if expr.syntax().text_range().start() != offset { return None; @@ -165,10 +183,10 @@ fn on_opening_brace_typed(file: &Parse, offset: TextSize) -> Option< return None; } - // Insert `}` right after the expression. + // Insert the closing bracket right after the expression. Some(TextEdit::insert( - expr.syntax().text_range().end() + TextSize::of("{"), - "}".to_string(), + expr.syntax().text_range().end() + TextSize::of(opening_bracket), + closing_bracket.to_string(), )) } } @@ -936,6 +954,193 @@ use some::pa$0th::to::Item; ); } + #[test] + fn adds_closing_parenthesis_for_expr() { + type_char( + '(', + r#" +fn f() { match () { _ => $0() } } + "#, + r#" +fn f() { match () { _ => (()) } } + "#, + ); + type_char( + '(', + r#" +fn f() { $0() } + "#, + r#" +fn f() { (()) } + "#, + ); + type_char( + '(', + r#" +fn f() { let x = $0(); } + "#, + r#" +fn f() { let x = (()); } + "#, + ); + type_char( + '(', + r#" +fn f() { let x = $0a.b(); } + "#, + r#" +fn f() { let x = (a.b()); } + "#, + ); + type_char( + '(', + r#" +const S: () = $0(); +fn f() {} + "#, + r#" +const S: () = (()); +fn f() {} + "#, + ); + type_char( + '(', + r#" +const S: () = $0a.b(); +fn f() {} + "#, + r#" +const S: () = (a.b()); +fn f() {} + "#, + ); + type_char( + '(', + r#" +fn f() { + match x { + 0 => $0(), + 1 => (), + } +} + "#, + r#" +fn f() { + match x { + 0 => (()), + 1 => (), + } +} + "#, + ); + type_char( + '(', + r#" + fn f() { + let z = Some($03); + } + "#, + r#" + fn f() { + let z = Some((3)); + } + "#, + ); + } + + #[test] + fn parenthesis_noop_in_string_literal() { + // Regression test for #9351 + type_char_noop( + '(', + r##" +fn check_with(ra_fixture: &str, expect: Expect) { + let base = r#" +enum E { T(), R$0, C } +use self::E::X; +const Z: E = E::C; +mod m {} +asdasdasdasdasdasda +sdasdasdasdasdasda +sdasdasdasdasd +"#; + let actual = completion_list(&format!("{}\n{}", base, ra_fixture)); + expect.assert_eq(&actual) +} + "##, + ); + } + + #[test] + fn parenthesis_noop_in_item_position_with_macro() { + type_char_noop('(', r#"$0println!();"#); + type_char_noop( + '(', + r#" +fn main() $0println!("hello"); +}"#, + ); + } + + #[test] + fn parenthesis_noop_in_use_tree() { + type_char_noop( + '(', + r#" +use some::$0Path; + "#, + ); + type_char_noop( + '(', + r#" +use some::{Path, $0Other}; + "#, + ); + type_char_noop( + '(', + r#" +use some::{$0Path, Other}; + "#, + ); + type_char_noop( + '(', + r#" +use some::path::$0to::Item; + "#, + ); + type_char_noop( + '(', + r#" +use some::$0path::to::Item; + "#, + ); + type_char_noop( + '(', + r#" +use $0some::path::to::Item; + "#, + ); + type_char_noop( + '(', + r#" +use some::path::$0to::{Item}; + "#, + ); + type_char_noop( + '(', + r#" +use $0Thing as _; + "#, + ); + + type_char_noop( + '(', + r#" +use some::pa$0th::to::Item; + "#, + ); + } + #[test] fn adds_closing_angle_bracket_for_generic_args() { type_char( diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index ab06b96814a..901a117e4d5 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs @@ -218,7 +218,7 @@ fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProvi } fn more_trigger_character(config: &Config) -> Vec { - let mut res = vec![".".to_string(), ">".to_string(), "{".to_string()]; + let mut res = vec![".".to_string(), ">".to_string(), "{".to_string(), "(".to_string()]; if config.snippet_cap() { res.push("<".to_string()); } From da786170f82717bba8086e6618382e57b5e98180 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 31 Aug 2023 10:15:31 +0300 Subject: [PATCH 055/250] Use proper assertion in on-type formatting edits Co-authored-by: DropDemBits --- crates/rust-analyzer/src/handlers/request.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 5f1f731cffb..a09a5e111ab 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -354,7 +354,7 @@ pub(crate) fn handle_on_type_formatting( // This should be a single-file edit let (_, (text_edit, snippet_edit)) = edit.source_file_edits.into_iter().next().unwrap(); - stdx::never!(snippet_edit.is_none(), "on type formatting shouldn't use structured snippets"); + stdx::always!(snippet_edit.is_none(), "on type formatting shouldn't use structured snippets"); let change = to_proto::snippet_text_edit_vec(&line_index, edit.is_snippet, text_edit); Ok(Some(change)) From c19390992ca2a3bc1d182798e59d56c8b477702d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 1 Sep 2023 21:42:51 +0200 Subject: [PATCH 056/250] Reduce semantic token cache lock scopes --- crates/rust-analyzer/src/handlers/request.rs | 20 ++++--- crates/rust-analyzer/src/main_loop.rs | 59 ++++++++++---------- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 5f1f731cffb..85ac690a411 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -1569,18 +1569,21 @@ pub(crate) fn handle_semantic_tokens_full_delta( snap.config.highlighting_non_standard_tokens(), ); - let mut cache = snap.semantic_tokens_cache.lock(); - let cached_tokens = cache.entry(params.text_document.uri).or_default(); + let cached_tokens = snap.semantic_tokens_cache.lock().remove(¶ms.text_document.uri); - if let Some(prev_id) = &cached_tokens.result_id { + if let Some(cached_tokens @ lsp_types::SemanticTokens { result_id: Some(prev_id), .. }) = + &cached_tokens + { if *prev_id == params.previous_result_id { let delta = to_proto::semantic_token_delta(cached_tokens, &semantic_tokens); - *cached_tokens = semantic_tokens; + snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens); return Ok(Some(delta.into())); } } - *cached_tokens = semantic_tokens.clone(); + // Clone first to keep the lock short + let semantic_tokens_clone = semantic_tokens.clone(); + snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens_clone); Ok(Some(semantic_tokens.into())) } @@ -1879,12 +1882,15 @@ fn run_rustfmt( // Determine the edition of the crate the file belongs to (if there's multiple, we pick the // highest edition). - let editions = snap + let Ok(editions) = snap .analysis .relevant_crates_for(file_id)? .into_iter() .map(|crate_id| snap.analysis.crate_edition(crate_id)) - .collect::, _>>()?; + .collect::, _>>() + else { + return Ok(None); + }; let edition = editions.iter().copied().max(); let line_index = snap.file_line_index(file_id)?; diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 74036710fa3..effa24c9371 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -670,6 +670,7 @@ impl GlobalState { } use crate::handlers::request as handlers; + use lsp_types::request as lsp_request; dispatcher // Request handlers that must run on the main thread @@ -682,30 +683,30 @@ impl GlobalState { // are run on the main thread to reduce latency: .on_sync::(handlers::handle_join_lines) .on_sync::(handlers::handle_on_enter) - .on_sync::(handlers::handle_selection_range) + .on_sync::(handlers::handle_selection_range) .on_sync::(handlers::handle_matching_brace) .on_sync::(handlers::handle_on_type_formatting) // Formatting should be done immediately as the editor might wait on it, but we can't // put it on the main thread as we do not want the main thread to block on rustfmt. // So we have an extra thread just for formatting requests to make sure it gets handled // as fast as possible. - .on_fmt_thread::(handlers::handle_formatting) - .on_fmt_thread::(handlers::handle_range_formatting) + .on_fmt_thread::(handlers::handle_formatting) + .on_fmt_thread::(handlers::handle_range_formatting) // We can’t run latency-sensitive request handlers which do semantic // analysis on the main thread because that would block other // requests. Instead, we run these request handlers on higher priority // threads in the threadpool. - .on_latency_sensitive::(handlers::handle_completion) - .on_latency_sensitive::( + .on_latency_sensitive::(handlers::handle_completion) + .on_latency_sensitive::( handlers::handle_completion_resolve, ) - .on_latency_sensitive::( + .on_latency_sensitive::( handlers::handle_semantic_tokens_full, ) - .on_latency_sensitive::( + .on_latency_sensitive::( handlers::handle_semantic_tokens_full_delta, ) - .on_latency_sensitive::( + .on_latency_sensitive::( handlers::handle_semantic_tokens_range, ) // All other request handlers @@ -729,29 +730,25 @@ impl GlobalState { .on::(handlers::handle_open_cargo_toml) .on::(handlers::handle_move_item) .on::(handlers::handle_workspace_symbol) - .on::(handlers::handle_document_symbol) - .on::(handlers::handle_goto_definition) - .on::(handlers::handle_goto_declaration) - .on::(handlers::handle_goto_implementation) - .on::(handlers::handle_goto_type_definition) - .on_no_retry::(handlers::handle_inlay_hints) - .on::(handlers::handle_inlay_hints_resolve) - .on::(handlers::handle_code_lens) - .on::(handlers::handle_code_lens_resolve) - .on::(handlers::handle_folding_range) - .on::(handlers::handle_signature_help) - .on::(handlers::handle_prepare_rename) - .on::(handlers::handle_rename) - .on::(handlers::handle_references) - .on::(handlers::handle_document_highlight) - .on::(handlers::handle_call_hierarchy_prepare) - .on::( - handlers::handle_call_hierarchy_incoming, - ) - .on::( - handlers::handle_call_hierarchy_outgoing, - ) - .on::(handlers::handle_will_rename_files) + .on::(handlers::handle_document_symbol) + .on::(handlers::handle_goto_definition) + .on::(handlers::handle_goto_declaration) + .on::(handlers::handle_goto_implementation) + .on::(handlers::handle_goto_type_definition) + .on_no_retry::(handlers::handle_inlay_hints) + .on::(handlers::handle_inlay_hints_resolve) + .on::(handlers::handle_code_lens) + .on::(handlers::handle_code_lens_resolve) + .on::(handlers::handle_folding_range) + .on::(handlers::handle_signature_help) + .on::(handlers::handle_prepare_rename) + .on::(handlers::handle_rename) + .on::(handlers::handle_references) + .on::(handlers::handle_document_highlight) + .on::(handlers::handle_call_hierarchy_prepare) + .on::(handlers::handle_call_hierarchy_incoming) + .on::(handlers::handle_call_hierarchy_outgoing) + .on::(handlers::handle_will_rename_files) .on::(handlers::handle_ssr) .on::(handlers::handle_view_recursive_memory_layout) .finish(); From 056b6b9416ab69e89c5aeb610590e5fc7e7e32ef Mon Sep 17 00:00:00 2001 From: cui fliter Date: Sat, 2 Sep 2023 14:18:10 +0800 Subject: [PATCH 057/250] remove the repetitive word Signed-off-by: cui fliter --- crates/hir-def/src/nameres.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index f2110410980..9a9fa0e02b0 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -93,7 +93,7 @@ use crate::{ #[derive(Debug, PartialEq, Eq)] pub struct DefMap { _c: Count, - /// When this is a block def map, this will hold the block id of the the block and module that + /// When this is a block def map, this will hold the block id of the block and module that /// contains this block. block: Option, /// The modules and their data declared in this crate. From 2dbc7e3e1a6ca07124d3aeaddd42aa8c0bce987d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 2 Sep 2023 14:01:58 +0200 Subject: [PATCH 058/250] Restructure some modules in rust-analyzer crate --- crates/rust-analyzer/src/caps.rs | 10 +- crates/rust-analyzer/src/cli/lsif.rs | 2 +- crates/rust-analyzer/src/diagnostics.rs | 40 ++++++- .../rust-analyzer/src/diagnostics/to_proto.rs | 4 +- crates/rust-analyzer/src/dispatch.rs | 2 +- crates/rust-analyzer/src/global_state.rs | 6 +- .../src/handlers/notification.rs | 8 +- crates/rust-analyzer/src/handlers/request.rs | 8 +- crates/rust-analyzer/src/lib.rs | 29 +---- crates/rust-analyzer/src/lsp.rs | 29 +++++ .../src/{lsp_ext.rs => lsp/ext.rs} | 0 .../rust-analyzer/src/{ => lsp}/from_proto.rs | 2 +- .../src/{ => lsp}/semantic_tokens.rs | 0 .../rust-analyzer/src/{ => lsp}/to_proto.rs | 11 +- .../src/{lsp_utils.rs => lsp/utils.rs} | 4 +- crates/rust-analyzer/src/main_loop.rs | 106 +++++------------- crates/rust-analyzer/src/markdown.rs | 1 + crates/rust-analyzer/src/reload.rs | 1 + crates/rust-analyzer/tests/slow-tests/main.rs | 2 +- .../rust-analyzer/tests/slow-tests/support.rs | 6 +- crates/rust-analyzer/tests/slow-tests/tidy.rs | 6 +- docs/dev/lsp-extensions.md | 2 +- 22 files changed, 145 insertions(+), 134 deletions(-) create mode 100644 crates/rust-analyzer/src/lsp.rs rename crates/rust-analyzer/src/{lsp_ext.rs => lsp/ext.rs} (100%) rename crates/rust-analyzer/src/{ => lsp}/from_proto.rs (99%) rename crates/rust-analyzer/src/{ => lsp}/semantic_tokens.rs (100%) rename crates/rust-analyzer/src/{ => lsp}/to_proto.rs (99%) rename crates/rust-analyzer/src/{lsp_utils.rs => lsp/utils.rs} (99%) diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index ab06b96814a..822811e67f5 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs @@ -16,10 +16,12 @@ use lsp_types::{ }; use serde_json::json; -use crate::config::{Config, RustfmtConfig}; -use crate::line_index::PositionEncoding; -use crate::lsp_ext::negotiated_encoding; -use crate::semantic_tokens; +use crate::{ + config::{Config, RustfmtConfig}, + line_index::PositionEncoding, + lsp::semantic_tokens, + lsp_ext::negotiated_encoding, +}; pub fn server_capabilities(config: &Config) -> ServerCapabilities { ServerCapabilities { diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs index 42d180114e5..d6a45ce06f2 100644 --- a/crates/rust-analyzer/src/cli/lsif.rs +++ b/crates/rust-analyzer/src/cli/lsif.rs @@ -21,7 +21,7 @@ use vfs::{AbsPathBuf, Vfs}; use crate::{ cli::flags, line_index::{LineEndings, LineIndex, PositionEncoding}, - to_proto, + lsp::to_proto, version::version, }; diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index b65f38a0c71..71701ef1617 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs @@ -9,7 +9,7 @@ use nohash_hasher::{IntMap, IntSet}; use rustc_hash::FxHashSet; use triomphe::Arc; -use crate::lsp_ext; +use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext}; pub(crate) type CheckFixes = Arc>>>; @@ -122,3 +122,41 @@ fn are_diagnostics_equal(left: &lsp_types::Diagnostic, right: &lsp_types::Diagno && left.range == right.range && left.message == right.message } + +pub(crate) fn fetch_native_diagnostics( + snapshot: GlobalStateSnapshot, + subscriptions: Vec, +) -> Vec<(FileId, Vec)> { + let _p = profile::span("fetch_native_diagnostics"); + let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned()); + subscriptions + .into_iter() + .filter_map(|file_id| { + let line_index = snapshot.file_line_index(file_id).ok()?; + let diagnostics = snapshot + .analysis + .diagnostics( + &snapshot.config.diagnostics(), + ide::AssistResolveStrategy::None, + file_id, + ) + .ok()? + .into_iter() + .map(move |d| lsp_types::Diagnostic { + range: lsp::to_proto::range(&line_index, d.range), + severity: Some(lsp::to_proto::diagnostic_severity(d.severity)), + code: Some(lsp_types::NumberOrString::String(d.code.as_str().to_string())), + code_description: Some(lsp_types::CodeDescription { + href: lsp_types::Url::parse(&d.code.url()).unwrap(), + }), + source: Some("rust-analyzer".to_string()), + message: d.message, + related_information: None, + tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::UNNECESSARY]), + data: None, + }) + .collect::>(); + Some((file_id, diagnostics)) + }) + .collect() +} diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 06564578d80..731580557c2 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -8,8 +8,8 @@ use stdx::format_to; use vfs::{AbsPath, AbsPathBuf}; use crate::{ - global_state::GlobalStateSnapshot, line_index::PositionEncoding, lsp_ext, - to_proto::url_from_abs_path, + global_state::GlobalStateSnapshot, line_index::PositionEncoding, + lsp::to_proto::url_from_abs_path, lsp_ext, }; use super::{DiagnosticsMapConfig, Fix}; diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index 5e5cd9a0269..7da43118881 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs @@ -8,9 +8,9 @@ use stdx::thread::ThreadIntent; use crate::{ global_state::{GlobalState, GlobalStateSnapshot}, + lsp::LspError, main_loop::Task, version::version, - LspError, }; /// A visitor for routing a raw JSON request to an appropriate handler function. diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 5024e04ffd3..22a7dd1548f 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -22,15 +22,14 @@ use vfs::{AnchoredPathBuf, Vfs}; use crate::{ config::{Config, ConfigError}, diagnostics::{CheckFixes, DiagnosticCollection}, - from_proto, line_index::{LineEndings, LineIndex}, + lsp::{from_proto, to_proto::url_from_abs_path}, lsp_ext, main_loop::Task, mem_docs::MemDocs, op_queue::OpQueue, reload, task_pool::TaskPool, - to_proto::url_from_abs_path, }; // Enforces drop order @@ -40,7 +39,7 @@ pub(crate) struct Handle { } pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response); -pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; +type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; /// `GlobalState` is the primary mutable state of the language server /// @@ -49,6 +48,7 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; /// incremental salsa database. /// /// Note that this struct has more than one impl in various modules! +#[doc(alias = "GlobalMess")] pub(crate) struct GlobalState { sender: Sender, req_queue: ReqQueue, diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index 3fd10274e0d..93f33d7cac9 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -13,8 +13,12 @@ use triomphe::Arc; use vfs::{AbsPathBuf, ChangeKind, VfsPath}; use crate::{ - config::Config, from_proto, global_state::GlobalState, lsp_ext::RunFlycheckParams, - lsp_utils::apply_document_changes, mem_docs::DocumentData, reload, + config::Config, + global_state::GlobalState, + lsp::{from_proto, utils::apply_document_changes}, + lsp_ext::RunFlycheckParams, + mem_docs::DocumentData, + reload, }; pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> anyhow::Result<()> { diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 85ac690a411..36158e8115e 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -36,15 +36,17 @@ use crate::{ cargo_target_spec::CargoTargetSpec, config::{Config, RustfmtConfig, WorkspaceSymbolConfig}, diff::diff, - from_proto, global_state::{GlobalState, GlobalStateSnapshot}, line_index::LineEndings, + lsp::{ + from_proto, to_proto, + utils::{all_edits_are_disjoint, invalid_params_error}, + LspError, + }, lsp_ext::{ self, CrateInfoResult, ExternalDocsPair, ExternalDocsResponse, FetchDependencyListParams, FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams, }, - lsp_utils::{all_edits_are_disjoint, invalid_params_error}, - to_proto, LspError, }; pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 57e26c241bb..04ac77b1f6c 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs @@ -23,18 +23,14 @@ mod cargo_target_spec; mod diagnostics; mod diff; mod dispatch; -mod from_proto; mod global_state; mod line_index; -mod lsp_utils; mod main_loop; mod markdown; mod mem_docs; mod op_queue; mod reload; -mod semantic_tokens; mod task_pool; -mod to_proto; mod version; mod handlers { @@ -43,13 +39,12 @@ mod handlers { } pub mod config; -pub mod lsp_ext; +pub mod lsp; +use self::lsp::ext as lsp_ext; #[cfg(test)] mod integrated_benchmarks; -use std::fmt; - use serde::de::DeserializeOwned; pub use crate::{caps::server_capabilities, main_loop::main_loop, version::version}; @@ -61,23 +56,3 @@ pub fn from_json( serde_json::from_value(json.clone()) .map_err(|e| anyhow::format_err!("Failed to deserialize {what}: {e}; {json}")) } - -#[derive(Debug)] -struct LspError { - code: i32, - message: String, -} - -impl LspError { - fn new(code: i32, message: String) -> LspError { - LspError { code, message } - } -} - -impl fmt::Display for LspError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Language Server request failed with {}. ({})", self.code, self.message) - } -} - -impl std::error::Error for LspError {} diff --git a/crates/rust-analyzer/src/lsp.rs b/crates/rust-analyzer/src/lsp.rs new file mode 100644 index 00000000000..ac7e1a95e62 --- /dev/null +++ b/crates/rust-analyzer/src/lsp.rs @@ -0,0 +1,29 @@ +//! Custom LSP definitions and protocol conversions. + +use core::fmt; + +pub(crate) mod utils; +pub(crate) mod semantic_tokens; +pub mod ext; +pub(crate) mod from_proto; +pub(crate) mod to_proto; + +#[derive(Debug)] +pub(crate) struct LspError { + pub(crate) code: i32, + pub(crate) message: String, +} + +impl LspError { + pub(crate) fn new(code: i32, message: String) -> LspError { + LspError { code, message } + } +} + +impl fmt::Display for LspError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Language Server request failed with {}. ({})", self.code, self.message) + } +} + +impl std::error::Error for LspError {} diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp/ext.rs similarity index 100% rename from crates/rust-analyzer/src/lsp_ext.rs rename to crates/rust-analyzer/src/lsp/ext.rs diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/lsp/from_proto.rs similarity index 99% rename from crates/rust-analyzer/src/from_proto.rs rename to crates/rust-analyzer/src/lsp/from_proto.rs index c247e1bb229..06efe6ca911 100644 --- a/crates/rust-analyzer/src/from_proto.rs +++ b/crates/rust-analyzer/src/lsp/from_proto.rs @@ -12,8 +12,8 @@ use crate::{ from_json, global_state::GlobalStateSnapshot, line_index::{LineIndex, PositionEncoding}, + lsp::utils::invalid_params_error, lsp_ext, - lsp_utils::invalid_params_error, }; pub(crate) fn abs_path(url: &lsp_types::Url) -> anyhow::Result { diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/lsp/semantic_tokens.rs similarity index 100% rename from crates/rust-analyzer/src/semantic_tokens.rs rename to crates/rust-analyzer/src/lsp/semantic_tokens.rs diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs similarity index 99% rename from crates/rust-analyzer/src/to_proto.rs rename to crates/rust-analyzer/src/lsp/to_proto.rs index 7b32180e3eb..daa7f5fe19d 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -22,9 +22,12 @@ use crate::{ config::{CallInfoConfig, Config}, global_state::GlobalStateSnapshot, line_index::{LineEndings, LineIndex, PositionEncoding}, + lsp::{ + semantic_tokens::{self, standard_fallback_type}, + utils::invalid_params_error, + LspError, + }, lsp_ext::{self, SnippetTextEdit}, - lsp_utils::invalid_params_error, - semantic_tokens::{self, standard_fallback_type}, }; pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { @@ -1425,8 +1428,8 @@ pub(crate) mod command { use crate::{ global_state::GlobalStateSnapshot, + lsp::to_proto::{location, location_link}, lsp_ext, - to_proto::{location, location_link}, }; pub(crate) fn show_references( @@ -1532,7 +1535,7 @@ pub(crate) fn markup_content( lsp_types::MarkupContent { kind, value } } -pub(crate) fn rename_error(err: RenameError) -> crate::LspError { +pub(crate) fn rename_error(err: RenameError) -> LspError { // This is wrong, but we don't have a better alternative I suppose? // https://github.com/microsoft/language-server-protocol/issues/1341 invalid_params_error(err.to_string()) diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp/utils.rs similarity index 99% rename from crates/rust-analyzer/src/lsp_utils.rs rename to crates/rust-analyzer/src/lsp/utils.rs index 74e79e8e605..b388b317599 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp/utils.rs @@ -6,10 +6,10 @@ use lsp_types::request::Request; use triomphe::Arc; use crate::{ - from_proto, global_state::GlobalState, line_index::{LineEndings, LineIndex, PositionEncoding}, - lsp_ext, LspError, + lsp::{from_proto, LspError}, + lsp_ext, }; pub(crate) fn invalid_params_error(message: String) -> LspError { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index effa24c9371..da4bef730b3 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -17,11 +17,14 @@ use vfs::FileId; use crate::{ config::Config, + diagnostics::fetch_native_diagnostics, dispatch::{NotificationDispatcher, RequestDispatcher}, - from_proto, global_state::{file_id_to_url, url_to_file_id, GlobalState}, + lsp::{ + from_proto, + utils::{notification_is, Progress}, + }, lsp_ext, - lsp_utils::{notification_is, Progress}, reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress}, }; @@ -420,6 +423,32 @@ impl GlobalState { }); } + fn update_diagnostics(&mut self) { + let db = self.analysis_host.raw_database(); + let subscriptions = self + .mem_docs + .iter() + .map(|path| self.vfs.read().0.file_id(path).unwrap()) + .filter(|&file_id| { + let source_root = db.file_source_root(file_id); + // Only publish diagnostics for files in the workspace, not from crates.io deps + // or the sysroot. + // While theoretically these should never have errors, we have quite a few false + // positives particularly in the stdlib, and those diagnostics would stay around + // forever if we emitted them here. + !db.source_root(source_root).is_library + }) + .collect::>(); + tracing::trace!("updating notifications for {:?}", subscriptions); + + // Diagnostics are triggered by the user typing + // so we run them on a latency sensitive thread. + self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, { + let snapshot = self.snapshot(); + move || Task::Diagnostics(fetch_native_diagnostics(snapshot, subscriptions)) + }); + } + fn update_status_or_notify(&mut self) { let status = self.current_status(); if self.last_reported_status.as_ref() != Some(&status) { @@ -785,77 +814,4 @@ impl GlobalState { .finish(); Ok(()) } - - fn update_diagnostics(&mut self) { - let db = self.analysis_host.raw_database(); - let subscriptions = self - .mem_docs - .iter() - .map(|path| self.vfs.read().0.file_id(path).unwrap()) - .filter(|&file_id| { - let source_root = db.file_source_root(file_id); - // Only publish diagnostics for files in the workspace, not from crates.io deps - // or the sysroot. - // While theoretically these should never have errors, we have quite a few false - // positives particularly in the stdlib, and those diagnostics would stay around - // forever if we emitted them here. - !db.source_root(source_root).is_library - }) - .collect::>(); - - tracing::trace!("updating notifications for {:?}", subscriptions); - - let snapshot = self.snapshot(); - - // Diagnostics are triggered by the user typing - // so we run them on a latency sensitive thread. - self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, move || { - let _p = profile::span("publish_diagnostics"); - let _ctx = stdx::panic_context::enter("publish_diagnostics".to_owned()); - let diagnostics = subscriptions - .into_iter() - .filter_map(|file_id| { - let line_index = snapshot.file_line_index(file_id).ok()?; - Some(( - file_id, - line_index, - snapshot - .analysis - .diagnostics( - &snapshot.config.diagnostics(), - ide::AssistResolveStrategy::None, - file_id, - ) - .ok()?, - )) - }) - .map(|(file_id, line_index, it)| { - ( - file_id, - it.into_iter() - .map(move |d| lsp_types::Diagnostic { - range: crate::to_proto::range(&line_index, d.range), - severity: Some(crate::to_proto::diagnostic_severity(d.severity)), - code: Some(lsp_types::NumberOrString::String( - d.code.as_str().to_string(), - )), - code_description: Some(lsp_types::CodeDescription { - href: lsp_types::Url::parse(&d.code.url()).unwrap(), - }), - source: Some("rust-analyzer".to_string()), - message: d.message, - related_information: None, - tags: if d.unused { - Some(vec![lsp_types::DiagnosticTag::UNNECESSARY]) - } else { - None - }, - data: None, - }) - .collect::>(), - ) - }); - Task::Diagnostics(diagnostics.collect()) - }); - } } diff --git a/crates/rust-analyzer/src/markdown.rs b/crates/rust-analyzer/src/markdown.rs index 58426c66a85..3201b0ed520 100644 --- a/crates/rust-analyzer/src/markdown.rs +++ b/crates/rust-analyzer/src/markdown.rs @@ -3,6 +3,7 @@ use ide_db::rust_doc::is_rust_fence; const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"]; +// FIXME: why is this in this crate? pub(crate) fn format_docs(src: &str) -> String { let mut processed_lines = Vec::new(); let mut in_code_block = false; diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 0a2bb822475..3fae08b82e2 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -12,6 +12,7 @@ //! correct. Instead, we try to provide a best-effort service. Even if the //! project is currently loading and we don't have a full project model, we //! still want to respond to various requests. +// FIXME: This is a mess that needs some untangling work use std::{iter, mem}; use flycheck::{FlycheckConfig, FlycheckHandle}; diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index ed6ef47c8e0..d5991429899 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -29,7 +29,7 @@ use lsp_types::{ PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams, }; -use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams}; +use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams}; use serde_json::json; use test_utils::skip_slow_tests; diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index 3c52ef5ef7f..e49b5768fa4 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -9,7 +9,7 @@ use std::{ use crossbeam_channel::{after, select, Receiver}; use lsp_server::{Connection, Message, Notification, Request}; use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url}; -use rust_analyzer::{config::Config, lsp_ext, main_loop}; +use rust_analyzer::{config::Config, lsp, main_loop}; use serde::Serialize; use serde_json::{json, to_string_pretty, Value}; use test_utils::FixtureWithProjectMeta; @@ -260,9 +260,9 @@ impl Server { Message::Notification(n) if n.method == "experimental/serverStatus" => { let status = n .clone() - .extract::("experimental/serverStatus") + .extract::("experimental/serverStatus") .unwrap(); - if status.health != lsp_ext::Health::Ok { + if status.health != lsp::ext::Health::Ok { panic!("server errored/warned while loading workspace: {:?}", status.message); } status.quiescent diff --git a/crates/rust-analyzer/tests/slow-tests/tidy.rs b/crates/rust-analyzer/tests/slow-tests/tidy.rs index f230cba2bf8..9d984f0833a 100644 --- a/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -35,7 +35,7 @@ fn check_lsp_extensions_docs() { let expected_hash = { let lsp_ext_rs = sh - .read_file(sourcegen::project_root().join("crates/rust-analyzer/src/lsp_ext.rs")) + .read_file(sourcegen::project_root().join("crates/rust-analyzer/src/lsp/ext.rs")) .unwrap(); stable_hash(lsp_ext_rs.as_str()) }; @@ -45,7 +45,7 @@ fn check_lsp_extensions_docs() { sh.read_file(sourcegen::project_root().join("docs/dev/lsp-extensions.md")).unwrap(); let text = lsp_extensions_md .lines() - .find_map(|line| line.strip_prefix("lsp_ext.rs hash:")) + .find_map(|line| line.strip_prefix("lsp/ext.rs hash:")) .unwrap() .trim(); u64::from_str_radix(text, 16).unwrap() @@ -54,7 +54,7 @@ fn check_lsp_extensions_docs() { if actual_hash != expected_hash { panic!( " -lsp_ext.rs was changed without touching lsp-extensions.md. +lsp/ext.rs was changed without touching lsp-extensions.md. Expected hash: {expected_hash:x} Actual hash: {actual_hash:x} diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 024acb87709..67d82a68548 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ $DIR/raw-idents.rs:8:22 + | +LL | println!("It is {r#type}"); + | --^^^^ + | | + | raw identifier used here in format string + | help: remove the `r#` + | + = note: identifiers in format strings can be keywords and don't need to be prefixed with `r#` + +error: invalid format string: raw identifiers are not supported + --> $DIR/raw-idents.rs:9:31 + | +LL | println!(r##"It still is {r#type}"##); + | --^^^^ + | | + | raw identifier used here in format string + | help: remove the `r#` + | + = note: identifiers in format strings can be keywords and don't need to be prefixed with `r#` + +error: invalid format string: raw identifiers are not supported + --> $DIR/raw-idents.rs:10:14 + | +LL | println!(concat!("{r#", "type}")); + | ^^^^^^^^^^^^^^^^^^^^^^^ raw identifier used here in format string + | + = note: identifiers in format strings can be keywords and don't need to be prefixed with `r#` + = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid format string: raw identifiers are not supported + --> $DIR/raw-idents.rs:11:16 + | +LL | println!("{\x72\x23type:?}"); + | --------^^^^ + | | + | raw identifier used here in format string + | help: remove the `r#` + | + = note: identifiers in format strings can be keywords and don't need to be prefixed with `r#` + +error: aborting due to 4 previous errors + From 5046889f431e4eb94a1add6a23f023ae5e461a16 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 6 Sep 2023 18:52:12 +0200 Subject: [PATCH 095/250] Don't allocate the format_args template string as an expression --- crates/hir-def/src/body/lower.rs | 50 +++++++++---------- crates/hir-def/src/body/tests.rs | 6 +-- crates/hir-def/src/hir/format_args.rs | 15 ++---- .../extract_expressions_from_format_string.rs | 6 --- 4 files changed, 31 insertions(+), 46 deletions(-) diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 9e2b6f1a864..38073ce3da5 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -32,7 +32,7 @@ use crate::{ hir::{ dummy_expr_id, format_args::{ - self, FormatAlignment, FormatArgsPiece, FormatArgument, FormatArgumentKind, + self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait, }, @@ -1568,6 +1568,24 @@ impl ExprCollector<'_> { // endregion: labels // region: format + fn expand_macros_to_string(&mut self, expr: ast::Expr) -> Option<(ast::String, bool)> { + let m = match expr { + ast::Expr::MacroExpr(m) => m, + ast::Expr::Literal(l) => { + return match l.kind() { + ast::LiteralKind::String(s) => Some((s, true)), + _ => None, + } + } + _ => return None, + }; + let e = m.macro_call()?; + let macro_ptr = AstPtr::new(&e); + let (exp, _) = self.collect_macro_call(e, macro_ptr, true, |this, expansion| { + expansion.and_then(|it| this.expand_macros_to_string(it)) + })?; + Some((exp, false)) + } fn collect_format_args( &mut self, @@ -1586,31 +1604,13 @@ impl ExprCollector<'_> { }); let template = f.template(); let fmt_snippet = template.as_ref().map(ToString::to_string); - - // FIXME: We shouldn't allocate this one, just resolve and expand the macros to fetch the - // string literal! - let expr = self.collect_expr_opt(template); - - let fmt = 'b: { - if let Expr::Literal(Literal::String(_)) = self.body[expr] { - let source = self.source_map.expr_map_back[expr].clone(); - let is_direct_literal = source.file_id == self.expander.current_file_id; - if let ast::Expr::Literal(l) = - source.value.to_node(&self.db.parse_or_expand(source.file_id)) - { - if let ast::LiteralKind::String(s) = l.kind() { - break 'b format_args::parse( - expr, - &s, - fmt_snippet, - args, - is_direct_literal, - |name| self.alloc_expr_desugared(Expr::Path(Path::from(name))), - ); - } - } + let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) { + Some((s, is_direct_literal)) => { + format_args::parse(&s, fmt_snippet, args, is_direct_literal, |name| { + self.alloc_expr_desugared(Expr::Path(Path::from(name))) + }) } - return self.missing_expr(); + None => FormatArgs { template: Default::default(), arguments: args.finish() }, }; // Create a list of all _unique_ (argument, format trait) combinations. diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs index 76880bf68ed..1658757d2b6 100644 --- a/crates/hir-def/src/body/tests.rs +++ b/crates/hir-def/src/body/tests.rs @@ -161,7 +161,7 @@ fn main() { let count = 10; builtin#lang(Arguments::new_v1_formatted)( &[ - "\"hello ", " ", " friends, we ", " ", "", "\"", + "\"hello ", " ", " friends, we ", " ", "", "\"", ], &[ builtin#lang(Argument::new_display)( @@ -172,7 +172,7 @@ fn main() { &are, ), builtin#lang(Argument::new_display)( &"!", - ), + ), ], &[ builtin#lang(Placeholder::new)( @@ -212,7 +212,7 @@ fn main() { 0u32, builtin#lang(Count::Implied), builtin#lang(Count::Implied), - ), + ), ], unsafe { builtin#lang(UnsafeArg::new)() diff --git a/crates/hir-def/src/hir/format_args.rs b/crates/hir-def/src/hir/format_args.rs index 6197ee16494..d8f8e6026a5 100644 --- a/crates/hir-def/src/hir/format_args.rs +++ b/crates/hir-def/src/hir/format_args.rs @@ -1,3 +1,4 @@ +//! Parses `format_args` input. use std::mem; use hir_expand::name::Name; @@ -12,7 +13,6 @@ mod parse; #[derive(Debug, Clone, PartialEq, Eq)] pub struct FormatArgs { - pub template_expr: ExprId, pub template: Box<[FormatArgsPiece]>, pub arguments: FormatArguments, } @@ -166,7 +166,6 @@ enum PositionUsedAs { use PositionUsedAs::*; pub(crate) fn parse( - expr: ExprId, s: &ast::String, fmt_snippet: Option, mut args: FormatArgumentsCollector, @@ -195,11 +194,7 @@ pub(crate) fn parse( let is_source_literal = parser.is_source_literal; if !parser.errors.is_empty() { // FIXME: Diagnose - return FormatArgs { - template_expr: expr, - template: Default::default(), - arguments: args.finish(), - }; + return FormatArgs { template: Default::default(), arguments: args.finish() }; } let to_span = |inner_span: parse::InnerSpan| { @@ -419,11 +414,7 @@ pub(crate) fn parse( // FIXME: Diagnose } - FormatArgs { - template_expr: expr, - template: template.into_boxed_slice(), - arguments: args.finish(), - } + FormatArgs { template: template.into_boxed_slice(), arguments: args.finish() } } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index 25c4b49c945..31a1ff496e1 100644 --- a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -34,23 +34,17 @@ pub(crate) fn extract_expressions_from_format_string( let fmt_string = ctx.find_token_at_offset::()?; let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?; - dbg!(); let expanded_t = ast::String::cast( ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone(), 0.into()), )?; - dbg!(); if !is_format_string(&expanded_t) { - dbg!(); return None; } - dbg!(); let (new_fmt, extracted_args) = parse_format_exprs(fmt_string.text()).ok()?; - dbg!(); if extracted_args.is_empty() { return None; } - dbg!(); acc.add( AssistId( From 96f19231d3d241c41540570f6caee29666aa9384 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 6 Sep 2023 19:31:48 +0200 Subject: [PATCH 096/250] Fix hir pretty printing emitting trailing whitespace --- crates/hir-def/src/body/pretty.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 31c2e9c1fb9..fad4d7a4da6 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -140,9 +140,14 @@ impl Printer<'_> { } fn newline(&mut self) { - match self.buf.chars().rev().find(|ch| *ch != ' ') { - Some('\n') | None => {} - _ => writeln!(self).unwrap(), + match self.buf.chars().rev().find_position(|ch| *ch != ' ') { + Some((_, '\n')) | None => {} + Some((idx, _)) => { + if idx != 0 { + self.buf.drain(self.buf.len() - idx..); + } + writeln!(self).unwrap() + } } } From 42f77f89ff39546cc8cf0f3a8d2513424d479a58 Mon Sep 17 00:00:00 2001 From: David Barsky Date: Wed, 6 Sep 2023 15:32:38 -0400 Subject: [PATCH 097/250] internal: use current subcrate's `rustfmt.toml` with all rustfmt configurations --- crates/rust-analyzer/src/handlers/request.rs | 55 +++++++++++--------- crates/rust-analyzer/src/lsp/from_proto.rs | 2 +- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 36158e8115e..4b81b40436a 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -974,7 +974,7 @@ pub(crate) fn handle_hover( PositionOrRange::Range(range) => range, }; - let file_range = from_proto::file_range(&snap, params.text_document, range)?; + let file_range = from_proto::file_range(&snap, ¶ms.text_document, range)?; let info = match snap.analysis.hover(&snap.config.hover(), file_range)? { None => return Ok(None), Some(info) => info, @@ -1130,7 +1130,7 @@ pub(crate) fn handle_code_action( let line_index = snap.file_line_index(from_proto::file_id(&snap, ¶ms.text_document.uri)?)?; - let frange = from_proto::file_range(&snap, params.text_document.clone(), params.range)?; + let frange = from_proto::file_range(&snap, ¶ms.text_document, params.range)?; let mut assists_config = snap.config.assist(); assists_config.allowed = params @@ -1383,7 +1383,7 @@ pub(crate) fn handle_ssr( let selections = params .selections .iter() - .map(|range| from_proto::file_range(&snap, params.position.text_document.clone(), *range)) + .map(|range| from_proto::file_range(&snap, ¶ms.position.text_document, *range)) .collect::, _>>()?; let position = from_proto::file_position(&snap, params.position)?; let source_change = snap.analysis.structural_search_replace( @@ -1403,7 +1403,7 @@ pub(crate) fn handle_inlay_hints( let document_uri = ¶ms.text_document.uri; let FileRange { file_id, range } = from_proto::file_range( &snap, - TextDocumentIdentifier::new(document_uri.to_owned()), + &TextDocumentIdentifier::new(document_uri.to_owned()), params.range, )?; let line_index = snap.file_line_index(file_id)?; @@ -1455,7 +1455,7 @@ pub(crate) fn handle_call_hierarchy_incoming( let item = params.item; let doc = TextDocumentIdentifier::new(item.uri); - let frange = from_proto::file_range(&snap, doc, item.selection_range)?; + let frange = from_proto::file_range(&snap, &doc, item.selection_range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; let call_items = match snap.analysis.incoming_calls(fpos)? { @@ -1490,7 +1490,7 @@ pub(crate) fn handle_call_hierarchy_outgoing( let item = params.item; let doc = TextDocumentIdentifier::new(item.uri); - let frange = from_proto::file_range(&snap, doc, item.selection_range)?; + let frange = from_proto::file_range(&snap, &doc, item.selection_range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; let call_items = match snap.analysis.outgoing_calls(fpos)? { @@ -1596,7 +1596,7 @@ pub(crate) fn handle_semantic_tokens_range( ) -> anyhow::Result> { let _p = profile::span("handle_semantic_tokens_range"); - let frange = from_proto::file_range(&snap, params.text_document, params.range)?; + let frange = from_proto::file_range(&snap, ¶ms.text_document, params.range)?; let text = snap.analysis.file_text(frange.file_id)?; let line_index = snap.file_line_index(frange.file_id)?; @@ -1679,7 +1679,7 @@ pub(crate) fn handle_move_item( ) -> anyhow::Result> { let _p = profile::span("handle_move_item"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; - let range = from_proto::file_range(&snap, params.text_document, params.range)?; + let range = from_proto::file_range(&snap, ¶ms.text_document, params.range)?; let direction = match params.direction { lsp_ext::MoveItemDirection::Up => ide::Direction::Up, @@ -1902,23 +1902,7 @@ fn run_rustfmt( let mut cmd = process::Command::new(toolchain::rustfmt()); cmd.envs(snap.config.extra_env()); cmd.args(extra_args); - // try to chdir to the file so we can respect `rustfmt.toml` - // FIXME: use `rustfmt --config-path` once - // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed - match text_document.uri.to_file_path() { - Ok(mut path) => { - // pop off file name - if path.pop() && path.is_dir() { - cmd.current_dir(path); - } - } - Err(_) => { - tracing::error!( - "Unable to get file path for {}, rustfmt.toml might be ignored", - text_document.uri - ); - } - } + if let Some(edition) = edition { cmd.arg("--edition"); cmd.arg(edition.to_string()); @@ -1937,7 +1921,7 @@ fn run_rustfmt( .into()); } - let frange = from_proto::file_range(snap, text_document, range)?; + let frange = from_proto::file_range(snap, &text_document, range)?; let start_line = line_index.index.line_col(frange.range.start()).line; let end_line = line_index.index.line_col(frange.range.end()).line; @@ -1956,12 +1940,31 @@ fn run_rustfmt( } RustfmtConfig::CustomCommand { command, args } => { let mut cmd = process::Command::new(command); + cmd.envs(snap.config.extra_env()); cmd.args(args); cmd } }; + // try to chdir to the file so we can respect `rustfmt.toml` + // FIXME: use `rustfmt --config-path` once + // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed + match text_document.uri.to_file_path() { + Ok(mut path) => { + // pop off file name + if path.pop() && path.is_dir() { + command.current_dir(path); + } + } + Err(_) => { + tracing::error!( + text_document = ?text_document.uri, + "Unable to get path, rustfmt.toml might be ignored" + ); + } + } + let mut rustfmt = command .stdin(Stdio::piped()) .stdout(Stdio::piped()) diff --git a/crates/rust-analyzer/src/lsp/from_proto.rs b/crates/rust-analyzer/src/lsp/from_proto.rs index 06efe6ca911..69d6aba94c3 100644 --- a/crates/rust-analyzer/src/lsp/from_proto.rs +++ b/crates/rust-analyzer/src/lsp/from_proto.rs @@ -72,7 +72,7 @@ pub(crate) fn file_position( pub(crate) fn file_range( snap: &GlobalStateSnapshot, - text_document_identifier: lsp_types::TextDocumentIdentifier, + text_document_identifier: &lsp_types::TextDocumentIdentifier, range: lsp_types::Range, ) -> anyhow::Result { file_range_uri(snap, &text_document_identifier.uri, range) From f13b184eb3ab0e9ec59c1671b2cfb027d8745ef0 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 6 Sep 2023 21:49:16 +0200 Subject: [PATCH 098/250] Implement `write_via_move` intrinsic for mir-eval --- .../hir-ty/src/consteval/tests/intrinsics.rs | 18 ++++++++++++++++++ crates/hir-ty/src/mir/eval/shim.rs | 16 ++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs index cc3a43fd9ae..44a4ac27af0 100644 --- a/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -586,6 +586,24 @@ fn write_bytes() { ); } +#[test] +fn write_via_move() { + check_number( + r#" + extern "rust-intrinsic" { + fn write_via_move(ptr: *mut T, value: T); + } + + const GOAL: i32 = unsafe { + let mut x = 2; + write_via_move(&mut x, 100); + x + }; + "#, + 100, + ); +} + #[test] fn copy() { check_number( diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 18396638940..803ef631f1e 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -1200,6 +1200,22 @@ impl Evaluator<'_> { let addr = Address::from_bytes(arg.interval.get(self)?)?; destination.write_from_interval(self, Interval { addr, size: destination.size }) } + "write_via_move" => { + let [ptr, val] = args else { + return Err(MirEvalError::TypeError("write_via_move args are not provided")); + }; + let dst = Address::from_bytes(ptr.get(self)?)?; + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError( + "write_via_copy generic arg is not provided", + )); + }; + let size = self.size_of_sized(ty, locals, "write_via_move ptr type")?; + Interval { addr: dst, size }.write_from_interval(self, val.interval)?; + Ok(()) + } "write_bytes" => { let [dst, val, count] = args else { return Err(MirEvalError::TypeError("write_bytes args are not provided")); From e4c469321ce1f3a3da429d01bc525efc19483b79 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 7 Sep 2023 01:08:47 +0330 Subject: [PATCH 099/250] Ignore enum variants in analysis stats of mir bodies --- crates/rust-analyzer/src/cli/analysis_stats.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 4a03be1893c..25ed57f18cc 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -317,9 +317,13 @@ impl flags::AnalysisStats { fn run_mir_lowering(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) { let mut sw = self.stop_watch(); - let all = bodies.len() as u64; + let mut all = 0; let mut fail = 0; for &body in bodies { + if matches!(body, DefWithBody::Variant(_)) { + continue; + } + all += 1; let Err(e) = db.mir_body(body.into()) else { continue; }; From 10b0cd7047184f29d8ecd5a4fd5864596f15ec19 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 7 Sep 2023 11:37:59 +0200 Subject: [PATCH 100/250] Replace format-args parser with upstream fork --- Cargo.lock | 77 +- Cargo.toml | 7 +- crates/hir-def/Cargo.toml | 7 +- crates/hir-def/src/hir/format_args.rs | 3 +- crates/hir-def/src/hir/format_args/parse.rs | 1023 ------------------- crates/hir-ty/Cargo.toml | 3 +- 6 files changed, 44 insertions(+), 1076 deletions(-) delete mode 100644 crates/hir-def/src/hir/format_args/parse.rs diff --git a/Cargo.lock b/Cargo.lock index 203d7348be1..bd6554bf889 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -541,7 +541,7 @@ dependencies = [ "mbe", "once_cell", "profile", - "ra-ap-rustc_lexer", + "ra-ap-rustc_parse_format", "rustc-hash", "smallvec", "stdx", @@ -1483,15 +1483,35 @@ dependencies = [ ] [[package]] -name = "ra-ap-rustc_lexer" -version = "0.1.0" +name = "ra-ap-rustc_index" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1c145702ed3f237918e512685185dc8a4d0edc3a5326c63d20361d8ba9b45b3" +checksum = "07b5fa61d34da18e148dc3a81f654488ea07f40938d8aefb17f8b64bb78c6120" dependencies = [ - "unic-emoji-char", + "arrayvec", + "smallvec", +] + +[[package]] +name = "ra-ap-rustc_lexer" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2e2f6b48422e4eed5218277ab7cc9733e60dd8f3167f4f36a49a0cafe4dc195" +dependencies = [ + "unicode-properties", "unicode-xid", ] +[[package]] +name = "ra-ap-rustc_parse_format" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7369ad01cc79f9e3513c9f6a6326f6b980100e4862a7ac71b9991c88108bb" +dependencies = [ + "ra-ap-rustc_index", + "ra-ap-rustc_lexer", +] + [[package]] name = "rayon" version = "1.7.0" @@ -2065,47 +2085,6 @@ version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e5df347f0bf3ec1d670aad6ca5c6a1859cd9ea61d2113125794654ccced68f" -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-emoji-char" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - [[package]] name = "unicase" version = "2.6.0" @@ -2136,6 +2115,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f91c8b21fbbaa18853c3d0801c78f4fc94cdb976699bb03e832e75f7fd22f0" + [[package]] name = "unicode-segmentation" version = "1.10.1" diff --git a/Cargo.toml b/Cargo.toml index e97e58f5276..cab88fc18ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -103,4 +103,9 @@ triomphe = { version = "0.1.8", default-features = false, features = ["std"] } # can't upgrade due to dashmap depending on 0.12.3 currently hashbrown = { version = "0.12.3", features = ["inline-more"], default-features = false } -rustc_lexer = { version = "0.1.0", package = "ra-ap-rustc_lexer" } +rustc_lexer = { version = "0.10.0", package = "ra-ap-rustc_lexer" } +rustc_parse_format = { version = "0.10.0", package = "ra-ap-rustc_parse_format", default-features = false } + +# Upstream broke this for us so we can't update it +rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false } +rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false } diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml index 4640ee5140f..8cf61ee04d4 100644 --- a/crates/hir-def/Cargo.toml +++ b/crates/hir-def/Cargo.toml @@ -31,9 +31,10 @@ smallvec.workspace = true hashbrown.workspace = true triomphe.workspace = true -rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false } -rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false } -rustc_lexer = { version = "0.1.0", package = "ra-ap-rustc_lexer" } +rustc_abi.workspace = true +rustc_index.workspace = true +rustc_parse_format.workspace = true + # local deps stdx.workspace = true diff --git a/crates/hir-def/src/hir/format_args.rs b/crates/hir-def/src/hir/format_args.rs index d8f8e6026a5..75025a984fc 100644 --- a/crates/hir-def/src/hir/format_args.rs +++ b/crates/hir-def/src/hir/format_args.rs @@ -2,6 +2,7 @@ use std::mem; use hir_expand::name::Name; +use rustc_parse_format as parse; use syntax::{ ast::{self, IsString}, AstToken, SmolStr, TextRange, @@ -9,8 +10,6 @@ use syntax::{ use crate::hir::ExprId; -mod parse; - #[derive(Debug, Clone, PartialEq, Eq)] pub struct FormatArgs { pub template: Box<[FormatArgsPiece]>, diff --git a/crates/hir-def/src/hir/format_args/parse.rs b/crates/hir-def/src/hir/format_args/parse.rs deleted file mode 100644 index 22efa3883d4..00000000000 --- a/crates/hir-def/src/hir/format_args/parse.rs +++ /dev/null @@ -1,1023 +0,0 @@ -//! Macro support for format strings -//! -//! These structures are used when parsing format strings for the compiler. -//! Parsing does not happen at runtime: structures of `std::fmt::rt` are -//! generated instead. - -// This is a copy of -// https://github.com/Veykril/rust/blob/b89d7d6882532686fd90a89cec1a0fd386f0ade3/compiler/rustc_parse_format/src/lib.rs#L999-L1000 -// with the dependency of rustc-data-structures stripped out. - -// #![doc( -// html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", -// html_playground_url = "https://play.rust-lang.org/", -// test(attr(deny(warnings))) -// )] -// #![deny(rustc::untranslatable_diagnostic)] -// #![deny(rustc::diagnostic_outside_of_impl)] -// We want to be able to build this crate with a stable compiler, so no -// `#![feature]` attributes should be added. -#![allow(dead_code, unreachable_pub)] - -use rustc_lexer::unescape; -pub use Alignment::*; -pub use Count::*; -pub use Piece::*; -pub use Position::*; - -use std::iter; -use std::str; -use std::string; - -// Note: copied from rustc_span -/// Range inside of a `Span` used for diagnostics when we only have access to relative positions. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct InnerSpan { - pub start: usize, - pub end: usize, -} - -impl InnerSpan { - pub fn new(start: usize, end: usize) -> InnerSpan { - InnerSpan { start, end } - } -} - -/// The location and before/after width of a character whose width has changed from its source code -/// representation -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct InnerWidthMapping { - /// Index of the character in the source - pub position: usize, - /// The inner width in characters - pub before: usize, - /// The transformed width in characters - pub after: usize, -} - -impl InnerWidthMapping { - pub fn new(position: usize, before: usize, after: usize) -> InnerWidthMapping { - InnerWidthMapping { position, before, after } - } -} - -/// Whether the input string is a literal. If yes, it contains the inner width mappings. -#[derive(Clone, PartialEq, Eq)] -enum InputStringKind { - NotALiteral, - Literal { width_mappings: Vec }, -} - -/// The type of format string that we are parsing. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum ParseMode { - /// A normal format string as per `format_args!`. - Format, - /// An inline assembly template string for `asm!`. - InlineAsm, -} - -#[derive(Copy, Clone)] -struct InnerOffset(usize); - -impl InnerOffset { - fn to(self, end: InnerOffset) -> InnerSpan { - InnerSpan::new(self.0, end.0) - } -} - -/// A piece is a portion of the format string which represents the next part -/// to emit. These are emitted as a stream by the `Parser` class. -#[derive(Clone, Debug, PartialEq)] -pub enum Piece<'a> { - /// A literal string which should directly be emitted - String(&'a str), - /// This describes that formatting should process the next argument (as - /// specified inside) for emission. - NextArgument(Box>), -} - -/// Representation of an argument specification. -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct Argument<'a> { - /// Where to find this argument - pub position: Position<'a>, - /// The span of the position indicator. Includes any whitespace in implicit - /// positions (`{ }`). - pub position_span: InnerSpan, - /// How to format the argument - pub format: FormatSpec<'a>, -} - -/// Specification for the formatting of an argument in the format string. -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct FormatSpec<'a> { - /// Optionally specified character to fill alignment with. - pub fill: Option, - /// Span of the optionally specified fill character. - pub fill_span: Option, - /// Optionally specified alignment. - pub align: Alignment, - /// The `+` or `-` flag. - pub sign: Option, - /// The `#` flag. - pub alternate: bool, - /// The `0` flag. - pub zero_pad: bool, - /// The `x` or `X` flag. (Only for `Debug`.) - pub debug_hex: Option, - /// The integer precision to use. - pub precision: Count<'a>, - /// The span of the precision formatting flag (for diagnostics). - pub precision_span: Option, - /// The string width requested for the resulting format. - pub width: Count<'a>, - /// The span of the width formatting flag (for diagnostics). - pub width_span: Option, - /// The descriptor string representing the name of the format desired for - /// this argument, this can be empty or any number of characters, although - /// it is required to be one word. - pub ty: &'a str, - /// The span of the descriptor string (for diagnostics). - pub ty_span: Option, -} - -/// Enum describing where an argument for a format can be located. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Position<'a> { - /// The argument is implied to be located at an index - ArgumentImplicitlyIs(usize), - /// The argument is located at a specific index given in the format, - ArgumentIs(usize), - /// The argument has a name. - ArgumentNamed(&'a str), -} - -impl Position<'_> { - pub fn index(&self) -> Option { - match self { - ArgumentIs(i, ..) | ArgumentImplicitlyIs(i) => Some(*i), - _ => None, - } - } -} - -/// Enum of alignments which are supported. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Alignment { - /// The value will be aligned to the left. - AlignLeft, - /// The value will be aligned to the right. - AlignRight, - /// The value will be aligned in the center. - AlignCenter, - /// The value will take on a default alignment. - AlignUnknown, -} - -/// Enum for the sign flags. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Sign { - /// The `+` flag. - Plus, - /// The `-` flag. - Minus, -} - -/// Enum for the debug hex flags. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum DebugHex { - /// The `x` flag in `{:x?}`. - Lower, - /// The `X` flag in `{:X?}`. - Upper, -} - -/// A count is used for the precision and width parameters of an integer, and -/// can reference either an argument or a literal integer. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Count<'a> { - /// The count is specified explicitly. - CountIs(usize), - /// The count is specified by the argument with the given name. - CountIsName(&'a str, InnerSpan), - /// The count is specified by the argument at the given index. - CountIsParam(usize), - /// The count is specified by a star (like in `{:.*}`) that refers to the argument at the given index. - CountIsStar(usize), - /// The count is implied and cannot be explicitly specified. - CountImplied, -} - -pub struct ParseError { - pub description: string::String, - pub note: Option, - pub label: string::String, - pub span: InnerSpan, - pub secondary_label: Option<(string::String, InnerSpan)>, - pub should_be_replaced_with_positional_argument: bool, -} - -/// The parser structure for interpreting the input format string. This is -/// modeled as an iterator over `Piece` structures to form a stream of tokens -/// being output. -/// -/// This is a recursive-descent parser for the sake of simplicity, and if -/// necessary there's probably lots of room for improvement performance-wise. -pub struct Parser<'a> { - mode: ParseMode, - input: &'a str, - cur: iter::Peekable>, - /// Error messages accumulated during parsing - pub errors: Vec, - /// Current position of implicit positional argument pointer - pub curarg: usize, - /// `Some(raw count)` when the string is "raw", used to position spans correctly - style: Option, - /// Start and end byte offset of every successfully parsed argument - pub arg_places: Vec, - /// Characters whose length has been changed from their in-code representation - width_map: Vec, - /// Span of the last opening brace seen, used for error reporting - last_opening_brace: Option, - /// Whether the source string is comes from `println!` as opposed to `format!` or `print!` - append_newline: bool, - /// Whether this formatting string was written directly in the source. This controls whether we - /// can use spans to refer into it and give better error messages. - /// N.B: This does _not_ control whether implicit argument captures can be used. - pub is_source_literal: bool, - /// Start position of the current line. - cur_line_start: usize, - /// Start and end byte offset of every line of the format string. Excludes - /// newline characters and leading whitespace. - pub line_spans: Vec, -} - -impl<'a> Iterator for Parser<'a> { - type Item = Piece<'a>; - - fn next(&mut self) -> Option> { - if let Some(&(pos, c)) = self.cur.peek() { - match c { - '{' => { - let curr_last_brace = self.last_opening_brace; - let byte_pos = self.to_span_index(pos); - let lbrace_end = InnerOffset(byte_pos.0 + self.to_span_width(pos)); - self.last_opening_brace = Some(byte_pos.to(lbrace_end)); - self.cur.next(); - if self.consume('{') { - self.last_opening_brace = curr_last_brace; - - Some(String(self.string(pos + 1))) - } else { - let arg = self.argument(lbrace_end); - if let Some(rbrace_pos) = self.consume_closing_brace(&arg) { - if self.is_source_literal { - let lbrace_byte_pos = self.to_span_index(pos); - let rbrace_byte_pos = self.to_span_index(rbrace_pos); - - let width = self.to_span_width(rbrace_pos); - - self.arg_places.push( - lbrace_byte_pos.to(InnerOffset(rbrace_byte_pos.0 + width)), - ); - } - } else { - if let Some(&(_, maybe)) = self.cur.peek() { - if maybe == '?' { - self.suggest_format(); - } else { - self.suggest_positional_arg_instead_of_captured_arg(arg); - } - } - } - Some(NextArgument(Box::new(arg))) - } - } - '}' => { - self.cur.next(); - if self.consume('}') { - Some(String(self.string(pos + 1))) - } else { - let err_pos = self.to_span_index(pos); - self.err_with_note( - "unmatched `}` found", - "unmatched `}`", - "if you intended to print `}`, you can escape it using `}}`", - err_pos.to(err_pos), - ); - None - } - } - _ => Some(String(self.string(pos))), - } - } else { - if self.is_source_literal { - let span = self.span(self.cur_line_start, self.input.len()); - if self.line_spans.last() != Some(&span) { - self.line_spans.push(span); - } - } - None - } - } -} - -impl<'a> Parser<'a> { - /// Creates a new parser for the given format string - pub fn new( - s: &'a str, - style: Option, - snippet: Option, - append_newline: bool, - mode: ParseMode, - ) -> Parser<'a> { - let input_string_kind = find_width_map_from_snippet(s, snippet, style); - let (width_map, is_source_literal) = match input_string_kind { - InputStringKind::Literal { width_mappings } => (width_mappings, true), - InputStringKind::NotALiteral => (Vec::new(), false), - }; - - Parser { - mode, - input: s, - cur: s.char_indices().peekable(), - errors: vec![], - curarg: 0, - style, - arg_places: vec![], - width_map, - last_opening_brace: None, - append_newline, - is_source_literal, - cur_line_start: 0, - line_spans: vec![], - } - } - - /// Notifies of an error. The message doesn't actually need to be of type - /// String, but I think it does when this eventually uses conditions so it - /// might as well start using it now. - fn err, S2: Into>( - &mut self, - description: S1, - label: S2, - span: InnerSpan, - ) { - self.errors.push(ParseError { - description: description.into(), - note: None, - label: label.into(), - span, - secondary_label: None, - should_be_replaced_with_positional_argument: false, - }); - } - - /// Notifies of an error. The message doesn't actually need to be of type - /// String, but I think it does when this eventually uses conditions so it - /// might as well start using it now. - fn err_with_note< - S1: Into, - S2: Into, - S3: Into, - >( - &mut self, - description: S1, - label: S2, - note: S3, - span: InnerSpan, - ) { - self.errors.push(ParseError { - description: description.into(), - note: Some(note.into()), - label: label.into(), - span, - secondary_label: None, - should_be_replaced_with_positional_argument: false, - }); - } - - /// Optionally consumes the specified character. If the character is not at - /// the current position, then the current iterator isn't moved and `false` is - /// returned, otherwise the character is consumed and `true` is returned. - fn consume(&mut self, c: char) -> bool { - self.consume_pos(c).is_some() - } - - /// Optionally consumes the specified character. If the character is not at - /// the current position, then the current iterator isn't moved and `None` is - /// returned, otherwise the character is consumed and the current position is - /// returned. - fn consume_pos(&mut self, c: char) -> Option { - if let Some(&(pos, maybe)) = self.cur.peek() { - if c == maybe { - self.cur.next(); - return Some(pos); - } - } - None - } - - fn remap_pos(&self, mut pos: usize) -> InnerOffset { - for width in &self.width_map { - if pos > width.position { - pos += width.before - width.after; - } else if pos == width.position && width.after == 0 { - pos += width.before; - } else { - break; - } - } - - InnerOffset(pos) - } - - fn to_span_index(&self, pos: usize) -> InnerOffset { - // This handles the raw string case, the raw argument is the number of # - // in r###"..."### (we need to add one because of the `r`). - let raw = self.style.map_or(0, |raw| raw + 1); - let pos = self.remap_pos(pos); - InnerOffset(raw + pos.0 + 1) - } - - fn to_span_width(&self, pos: usize) -> usize { - let pos = self.remap_pos(pos); - match self.width_map.iter().find(|w| w.position == pos.0) { - Some(w) => w.before, - None => 1, - } - } - - fn span(&self, start_pos: usize, end_pos: usize) -> InnerSpan { - let start = self.to_span_index(start_pos); - let end = self.to_span_index(end_pos); - start.to(end) - } - - /// Forces consumption of the specified character. If the character is not - /// found, an error is emitted. - fn consume_closing_brace(&mut self, arg: &Argument<'_>) -> Option { - self.ws(); - - let pos; - let description; - - if let Some(&(peek_pos, maybe)) = self.cur.peek() { - if maybe == '}' { - self.cur.next(); - return Some(peek_pos); - } - - pos = peek_pos; - description = format!("expected `'}}'`, found `{maybe:?}`"); - } else { - description = "expected `'}'` but string was terminated".to_owned(); - // point at closing `"` - pos = self.input.len() - if self.append_newline { 1 } else { 0 }; - } - - let pos = self.to_span_index(pos); - - let label = "expected `'}'`".to_owned(); - let (note, secondary_label) = if arg.format.fill == Some('}') { - ( - Some("the character `'}'` is interpreted as a fill character because of the `:` that precedes it".to_owned()), - arg.format.fill_span.map(|sp| ("this is not interpreted as a formatting closing brace".to_owned(), sp)), - ) - } else { - ( - Some("if you intended to print `{`, you can escape it using `{{`".to_owned()), - self.last_opening_brace.map(|sp| ("because of this opening brace".to_owned(), sp)), - ) - }; - - self.errors.push(ParseError { - description, - note, - label, - span: pos.to(pos), - secondary_label, - should_be_replaced_with_positional_argument: false, - }); - - None - } - - /// Consumes all whitespace characters until the first non-whitespace character - fn ws(&mut self) { - while let Some(&(_, c)) = self.cur.peek() { - if c.is_whitespace() { - self.cur.next(); - } else { - break; - } - } - } - - /// Parses all of a string which is to be considered a "raw literal" in a - /// format string. This is everything outside of the braces. - fn string(&mut self, start: usize) -> &'a str { - // we may not consume the character, peek the iterator - while let Some(&(pos, c)) = self.cur.peek() { - match c { - '{' | '}' => { - return &self.input[start..pos]; - } - '\n' if self.is_source_literal => { - self.line_spans.push(self.span(self.cur_line_start, pos)); - self.cur_line_start = pos + 1; - self.cur.next(); - } - _ => { - if self.is_source_literal && pos == self.cur_line_start && c.is_whitespace() { - self.cur_line_start = pos + c.len_utf8(); - } - self.cur.next(); - } - } - } - &self.input[start..self.input.len()] - } - - /// Parses an `Argument` structure, or what's contained within braces inside the format string. - fn argument(&mut self, start: InnerOffset) -> Argument<'a> { - let pos = self.position(); - - let end = self - .cur - .clone() - .find(|(_, ch)| !ch.is_whitespace()) - .map_or(start, |(end, _)| self.to_span_index(end)); - let position_span = start.to(end); - - let format = match self.mode { - ParseMode::Format => self.format(), - ParseMode::InlineAsm => self.inline_asm(), - }; - - // Resolve position after parsing format spec. - let pos = match pos { - Some(position) => position, - None => { - let i = self.curarg; - self.curarg += 1; - ArgumentImplicitlyIs(i) - } - }; - - Argument { position: pos, position_span, format } - } - - /// Parses a positional argument for a format. This could either be an - /// integer index of an argument, a named argument, or a blank string. - /// Returns `Some(parsed_position)` if the position is not implicitly - /// consuming a macro argument, `None` if it's the case. - fn position(&mut self) -> Option> { - if let Some(i) = self.integer() { - Some(ArgumentIs(i)) - } else { - match self.cur.peek() { - Some(&(_, c)) if rustc_lexer::is_id_start(c) => Some(ArgumentNamed(self.word())), - - // This is an `ArgumentNext`. - // Record the fact and do the resolution after parsing the - // format spec, to make things like `{:.*}` work. - _ => None, - } - } - } - - fn current_pos(&mut self) -> usize { - if let Some(&(pos, _)) = self.cur.peek() { - pos - } else { - self.input.len() - } - } - - /// Parses a format specifier at the current position, returning all of the - /// relevant information in the `FormatSpec` struct. - fn format(&mut self) -> FormatSpec<'a> { - let mut spec = FormatSpec { - fill: None, - fill_span: None, - align: AlignUnknown, - sign: None, - alternate: false, - zero_pad: false, - debug_hex: None, - precision: CountImplied, - precision_span: None, - width: CountImplied, - width_span: None, - ty: &self.input[..0], - ty_span: None, - }; - if !self.consume(':') { - return spec; - } - - // fill character - if let Some(&(idx, c)) = self.cur.peek() { - if let Some((_, '>' | '<' | '^')) = self.cur.clone().nth(1) { - spec.fill = Some(c); - spec.fill_span = Some(self.span(idx, idx + 1)); - self.cur.next(); - } - } - // Alignment - if self.consume('<') { - spec.align = AlignLeft; - } else if self.consume('>') { - spec.align = AlignRight; - } else if self.consume('^') { - spec.align = AlignCenter; - } - // Sign flags - if self.consume('+') { - spec.sign = Some(Sign::Plus); - } else if self.consume('-') { - spec.sign = Some(Sign::Minus); - } - // Alternate marker - if self.consume('#') { - spec.alternate = true; - } - // Width and precision - let mut havewidth = false; - - if self.consume('0') { - // small ambiguity with '0$' as a format string. In theory this is a - // '0' flag and then an ill-formatted format string with just a '$' - // and no count, but this is better if we instead interpret this as - // no '0' flag and '0$' as the width instead. - if let Some(end) = self.consume_pos('$') { - spec.width = CountIsParam(0); - spec.width_span = Some(self.span(end - 1, end + 1)); - havewidth = true; - } else { - spec.zero_pad = true; - } - } - - if !havewidth { - let start = self.current_pos(); - spec.width = self.count(start); - if spec.width != CountImplied { - let end = self.current_pos(); - spec.width_span = Some(self.span(start, end)); - } - } - - if let Some(start) = self.consume_pos('.') { - if self.consume('*') { - // Resolve `CountIsNextParam`. - // We can do this immediately as `position` is resolved later. - let i = self.curarg; - self.curarg += 1; - spec.precision = CountIsStar(i); - } else { - spec.precision = self.count(start + 1); - } - let end = self.current_pos(); - spec.precision_span = Some(self.span(start, end)); - } - - let ty_span_start = self.current_pos(); - // Optional radix followed by the actual format specifier - if self.consume('x') { - if self.consume('?') { - spec.debug_hex = Some(DebugHex::Lower); - spec.ty = "?"; - } else { - spec.ty = "x"; - } - } else if self.consume('X') { - if self.consume('?') { - spec.debug_hex = Some(DebugHex::Upper); - spec.ty = "?"; - } else { - spec.ty = "X"; - } - } else if self.consume('?') { - spec.ty = "?"; - } else { - spec.ty = self.word(); - if !spec.ty.is_empty() { - let ty_span_end = self.current_pos(); - spec.ty_span = Some(self.span(ty_span_start, ty_span_end)); - } - } - spec - } - - /// Parses an inline assembly template modifier at the current position, returning the modifier - /// in the `ty` field of the `FormatSpec` struct. - fn inline_asm(&mut self) -> FormatSpec<'a> { - let mut spec = FormatSpec { - fill: None, - fill_span: None, - align: AlignUnknown, - sign: None, - alternate: false, - zero_pad: false, - debug_hex: None, - precision: CountImplied, - precision_span: None, - width: CountImplied, - width_span: None, - ty: &self.input[..0], - ty_span: None, - }; - if !self.consume(':') { - return spec; - } - - let ty_span_start = self.current_pos(); - spec.ty = self.word(); - if !spec.ty.is_empty() { - let ty_span_end = self.current_pos(); - spec.ty_span = Some(self.span(ty_span_start, ty_span_end)); - } - - spec - } - - /// Parses a `Count` parameter at the current position. This does not check - /// for 'CountIsNextParam' because that is only used in precision, not - /// width. - fn count(&mut self, start: usize) -> Count<'a> { - if let Some(i) = self.integer() { - if self.consume('$') { - CountIsParam(i) - } else { - CountIs(i) - } - } else { - let tmp = self.cur.clone(); - let word = self.word(); - if word.is_empty() { - self.cur = tmp; - CountImplied - } else if let Some(end) = self.consume_pos('$') { - let name_span = self.span(start, end); - CountIsName(word, name_span) - } else { - self.cur = tmp; - CountImplied - } - } - } - - /// Parses a word starting at the current position. A word is the same as - /// Rust identifier, except that it can't start with `_` character. - fn word(&mut self) -> &'a str { - let start = match self.cur.peek() { - Some(&(pos, c)) if rustc_lexer::is_id_start(c) => { - self.cur.next(); - pos - } - _ => { - return ""; - } - }; - let mut end = None; - while let Some(&(pos, c)) = self.cur.peek() { - if rustc_lexer::is_id_continue(c) { - self.cur.next(); - } else { - end = Some(pos); - break; - } - } - let end = end.unwrap_or(self.input.len()); - let word = &self.input[start..end]; - if word == "_" { - self.err_with_note( - "invalid argument name `_`", - "invalid argument name", - "argument name cannot be a single underscore", - self.span(start, end), - ); - } - word - } - - fn integer(&mut self) -> Option { - let mut cur: usize = 0; - let mut found = false; - let mut overflow = false; - let start = self.current_pos(); - while let Some(&(_, c)) = self.cur.peek() { - if let Some(i) = c.to_digit(10) { - let (tmp, mul_overflow) = cur.overflowing_mul(10); - let (tmp, add_overflow) = tmp.overflowing_add(i as usize); - if mul_overflow || add_overflow { - overflow = true; - } - cur = tmp; - found = true; - self.cur.next(); - } else { - break; - } - } - - if overflow { - let end = self.current_pos(); - let overflowed_int = &self.input[start..end]; - self.err( - format!( - "integer `{}` does not fit into the type `usize` whose range is `0..={}`", - overflowed_int, - usize::MAX - ), - "integer out of range for `usize`", - self.span(start, end), - ); - } - - found.then_some(cur) - } - - fn suggest_format(&mut self) { - if let (Some(pos), Some(_)) = (self.consume_pos('?'), self.consume_pos(':')) { - let word = self.word(); - let _end = self.current_pos(); - let pos = self.to_span_index(pos); - self.errors.insert( - 0, - ParseError { - description: "expected format parameter to occur after `:`".to_owned(), - note: Some(format!("`?` comes after `:`, try `{}:{}` instead", word, "?")), - label: "expected `?` to occur after `:`".to_owned(), - span: pos.to(pos), - secondary_label: None, - should_be_replaced_with_positional_argument: false, - }, - ); - } - } - - fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: Argument<'a>) { - if let Some(end) = self.consume_pos('.') { - let byte_pos = self.to_span_index(end); - let start = InnerOffset(byte_pos.0 + 1); - let field = self.argument(start); - // We can only parse `foo.bar` field access, any deeper nesting, - // or another type of expression, like method calls, are not supported - if !self.consume('}') { - return; - } - if let ArgumentNamed(_) = arg.position { - if let ArgumentNamed(_) = field.position { - self.errors.insert( - 0, - ParseError { - description: "field access isn't supported".to_string(), - note: None, - label: "not supported".to_string(), - span: InnerSpan::new(arg.position_span.start, field.position_span.end), - secondary_label: None, - should_be_replaced_with_positional_argument: true, - }, - ); - } - } - } - } -} - -/// Finds the indices of all characters that have been processed and differ between the actual -/// written code (code snippet) and the `InternedString` that gets processed in the `Parser` -/// in order to properly synthesise the intra-string `Span`s for error diagnostics. -fn find_width_map_from_snippet( - input: &str, - snippet: Option, - str_style: Option, -) -> InputStringKind { - let snippet = match snippet { - Some(ref s) if s.starts_with('"') || s.starts_with("r\"") || s.starts_with("r#") => s, - _ => return InputStringKind::NotALiteral, - }; - - if str_style.is_some() { - return InputStringKind::Literal { width_mappings: Vec::new() }; - } - - // Strip quotes. - let snippet = &snippet[1..snippet.len() - 1]; - - // Macros like `println` add a newline at the end. That technically doesn't make them "literals" anymore, but it's fine - // since we will never need to point our spans there, so we lie about it here by ignoring it. - // Since there might actually be newlines in the source code, we need to normalize away all trailing newlines. - // If we only trimmed it off the input, `format!("\n")` would cause a mismatch as here we they actually match up. - // Alternatively, we could just count the trailing newlines and only trim one from the input if they don't match up. - let input_no_nl = input.trim_end_matches('\n'); - let Some(unescaped) = unescape_string(snippet) else { - return InputStringKind::NotALiteral; - }; - - let unescaped_no_nl = unescaped.trim_end_matches('\n'); - - if unescaped_no_nl != input_no_nl { - // The source string that we're pointing at isn't our input, so spans pointing at it will be incorrect. - // This can for example happen with proc macros that respan generated literals. - return InputStringKind::NotALiteral; - } - - let mut s = snippet.char_indices(); - let mut width_mappings = vec![]; - while let Some((pos, c)) = s.next() { - match (c, s.clone().next()) { - // skip whitespace and empty lines ending in '\\' - ('\\', Some((_, '\n'))) => { - let _ = s.next(); - let mut width = 2; - - while let Some((_, c)) = s.clone().next() { - if matches!(c, ' ' | '\n' | '\t') { - width += 1; - let _ = s.next(); - } else { - break; - } - } - - width_mappings.push(InnerWidthMapping::new(pos, width, 0)); - } - ('\\', Some((_, 'n' | 't' | 'r' | '0' | '\\' | '\'' | '\"'))) => { - width_mappings.push(InnerWidthMapping::new(pos, 2, 1)); - let _ = s.next(); - } - ('\\', Some((_, 'x'))) => { - // consume `\xAB` literal - s.nth(2); - width_mappings.push(InnerWidthMapping::new(pos, 4, 1)); - } - ('\\', Some((_, 'u'))) => { - let mut width = 2; - let _ = s.next(); - - if let Some((_, next_c)) = s.next() { - if next_c == '{' { - // consume up to 6 hexanumeric chars - let digits_len = - s.clone().take(6).take_while(|(_, c)| c.is_digit(16)).count(); - - let len_utf8 = s - .as_str() - .get(..digits_len) - .and_then(|digits| u32::from_str_radix(digits, 16).ok()) - .and_then(char::from_u32) - .map_or(1, char::len_utf8); - - // Skip the digits, for chars that encode to more than 1 utf-8 byte - // exclude as many digits as it is greater than 1 byte - // - // So for a 3 byte character, exclude 2 digits - let required_skips = digits_len.saturating_sub(len_utf8.saturating_sub(1)); - - // skip '{' and '}' also - width += required_skips + 2; - - s.nth(digits_len); - } else if next_c.is_digit(16) { - width += 1; - - // We suggest adding `{` and `}` when appropriate, accept it here as if - // it were correct - let mut i = 0; // consume up to 6 hexanumeric chars - while let (Some((_, c)), _) = (s.next(), i < 6) { - if c.is_digit(16) { - width += 1; - } else { - break; - } - i += 1; - } - } - } - - width_mappings.push(InnerWidthMapping::new(pos, width, 1)); - } - _ => {} - } - } - - InputStringKind::Literal { width_mappings } -} - -fn unescape_string(string: &str) -> Option { - let mut buf = string::String::new(); - let mut ok = true; - unescape::unescape_literal(string, unescape::Mode::Str, &mut |_, unescaped_char| { - match unescaped_char { - Ok(c) => buf.push(c), - Err(_) => ok = false, - } - }); - - ok.then_some(buf) -} diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index abc19d63abf..b95ae05ccd4 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -32,7 +32,8 @@ once_cell = "1.17.0" triomphe.workspace = true nohash-hasher.workspace = true typed-arena = "2.0.1" -rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false } + +rustc_index.workspace = true # local deps stdx.workspace = true From 5b5bce8aafe275502b773d5950165a3993251147 Mon Sep 17 00:00:00 2001 From: David Barsky Date: Tue, 5 Sep 2023 15:21:14 -0400 Subject: [PATCH 101/250] project-model: when using `rust-project.json`, prefer the sysroot-defined rustc over an env-based one --- crates/project-model/src/rustc_cfg.rs | 22 +++++++++++++++++++--- crates/project-model/src/sysroot.rs | 11 ++++++++++- crates/project-model/src/workspace.rs | 20 ++++++++++++-------- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/crates/project-model/src/rustc_cfg.rs b/crates/project-model/src/rustc_cfg.rs index 8392718b227..f8f9a5d5447 100644 --- a/crates/project-model/src/rustc_cfg.rs +++ b/crates/project-model/src/rustc_cfg.rs @@ -4,10 +4,11 @@ use std::process::Command; use rustc_hash::FxHashMap; -use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath}; +use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath, Sysroot}; pub(crate) fn get( cargo_toml: Option<&ManifestPath>, + sysroot: Option<&Sysroot>, target: Option<&str>, extra_env: &FxHashMap, ) -> Vec { @@ -25,7 +26,7 @@ pub(crate) fn get( // Add miri cfg, which is useful for mir eval in stdlib res.push(CfgFlag::Atom("miri".into())); - match get_rust_cfgs(cargo_toml, target, extra_env) { + match get_rust_cfgs(cargo_toml, sysroot, target, extra_env) { Ok(rustc_cfgs) => { tracing::debug!( "rustc cfgs found: {:?}", @@ -44,6 +45,7 @@ pub(crate) fn get( fn get_rust_cfgs( cargo_toml: Option<&ManifestPath>, + sysroot: Option<&Sysroot>, target: Option<&str>, extra_env: &FxHashMap, ) -> anyhow::Result { @@ -62,8 +64,22 @@ fn get_rust_cfgs( Err(e) => tracing::debug!("{e:?}: falling back to querying rustc for cfgs"), } } + + let rustc = match sysroot { + Some(sysroot) => { + let rustc = sysroot.discover_rustc()?.into(); + tracing::debug!(?rustc, "using rustc from sysroot"); + rustc + } + None => { + let rustc = toolchain::rustc(); + tracing::debug!(?rustc, "using rustc from env"); + rustc + } + }; + // using unstable cargo features failed, fall back to using plain rustc - let mut cmd = Command::new(toolchain::rustc()); + let mut cmd = Command::new(rustc); cmd.envs(extra_env); cmd.args(["--print", "cfg", "-O"]); if let Some(target) = target { diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index da862c9e87f..fe046dd1463 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -115,10 +115,19 @@ impl Sysroot { Ok(Sysroot::load(sysroot_dir, src)) } - pub fn discover_rustc(&self) -> Option { + pub fn discover_rustc_src(&self) -> Option { get_rustc_src(&self.root) } + pub fn discover_rustc(&self) -> Result { + let rustc = self.root.join("bin/rustc"); + tracing::debug!(?rustc, "checking for rustc binary at location"); + match fs::metadata(&rustc) { + Ok(_) => Ok(rustc), + Err(e) => Err(e), + } + } + pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result { let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| { format_err!("can't load standard library from sysroot path {sysroot_dir}") diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 13463e9f72e..0eb88332908 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -240,9 +240,9 @@ impl ProjectWorkspace { Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone()) .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))), Some(RustLibSource::Discover) => { - sysroot.as_ref().ok().and_then(Sysroot::discover_rustc).ok_or_else(|| { - Some(format!("Failed to discover rustc source for sysroot.")) - }) + sysroot.as_ref().ok().and_then(Sysroot::discover_rustc_src).ok_or_else( + || Some(format!("Failed to discover rustc source for sysroot.")), + ) } None => Err(None), }; @@ -279,8 +279,12 @@ impl ProjectWorkspace { } }); - let rustc_cfg = - rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env); + let rustc_cfg = rustc_cfg::get( + Some(&cargo_toml), + None, + config.target.as_deref(), + &config.extra_env, + ); let cfg_overrides = config.cfg_overrides.clone(); let data_layout = target_data_layout::get( @@ -335,7 +339,7 @@ impl ProjectWorkspace { tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); } - let rustc_cfg = rustc_cfg::get(None, target, extra_env); + let rustc_cfg = rustc_cfg::get(None, sysroot.as_ref().ok(), target, extra_env); ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg, toolchain } } @@ -360,7 +364,7 @@ impl ProjectWorkspace { if let Ok(sysroot) = &sysroot { tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); } - let rustc_cfg = rustc_cfg::get(None, None, &Default::default()); + let rustc_cfg = rustc_cfg::get(None, sysroot.as_ref().ok(), None, &Default::default()); Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg }) } @@ -756,7 +760,7 @@ fn project_json_to_crate_graph( let target_cfgs = match target.as_deref() { Some(target) => cfg_cache .entry(target) - .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)), + .or_insert_with(|| rustc_cfg::get(None, sysroot, Some(target), extra_env)), None => &rustc_cfg, }; From cd53bd6b8edb4e71954f9964f72d8126d83f4627 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 7 Sep 2023 18:40:53 +0200 Subject: [PATCH 102/250] Remove allocation on mir eval memory write --- crates/hir-ty/src/mir/eval.rs | 46 ++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 3944feb128c..5b267641a3d 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -215,9 +215,7 @@ impl Interval { } fn write_from_interval(&self, memory: &mut Evaluator<'_>, interval: Interval) -> Result<()> { - // FIXME: this could be more efficient - let bytes = &interval.get(memory)?.to_vec(); - memory.write_memory(self.addr, bytes) + memory.copy_from_interval(self.addr, interval) } fn slice(self, range: Range) -> Interval { @@ -1760,6 +1758,48 @@ impl Evaluator<'_> { Ok(()) } + fn copy_from_interval(&mut self, addr: Address, r: Interval) -> Result<()> { + if r.size == 0 { + return Ok(()); + } + + let oob = || MirEvalError::UndefinedBehavior("out of bounds memory write".to_string()); + + match (addr, r.addr) { + (Stack(dst), Stack(src)) => { + if self.stack.len() < src + r.size || self.stack.len() < dst + r.size { + return Err(oob()); + } + self.stack.copy_within(src..src + r.size, dst) + } + (Heap(dst), Heap(src)) => { + if self.stack.len() < src + r.size || self.stack.len() < dst + r.size { + return Err(oob()); + } + self.heap.copy_within(src..src + r.size, dst) + } + (Stack(dst), Heap(src)) => { + self.stack + .get_mut(dst..dst + r.size) + .ok_or_else(oob)? + .copy_from_slice(self.heap.get(src..src + r.size).ok_or_else(oob)?); + } + (Heap(dst), Stack(src)) => { + self.heap + .get_mut(dst..dst + r.size) + .ok_or_else(oob)? + .copy_from_slice(self.stack.get(src..src + r.size).ok_or_else(oob)?); + } + _ => { + return Err(MirEvalError::UndefinedBehavior(format!( + "invalid memory write at address {addr:?}" + ))) + } + } + + Ok(()) + } + fn size_align_of(&self, ty: &Ty, locals: &Locals) -> Result> { if let Some(layout) = self.layout_cache.borrow().get(ty) { return Ok(layout From 553152e2d568dd06406bd1cb93c2a6fe86a579e6 Mon Sep 17 00:00:00 2001 From: David Barsky Date: Thu, 7 Sep 2023 15:06:51 -0400 Subject: [PATCH 103/250] refactor `rustc_cfg` to be more inline with `docs/dev/style.md` --- crates/project-model/src/rustc_cfg.rs | 90 +++++++++++++++------------ crates/project-model/src/workspace.rs | 45 ++++++++++---- 2 files changed, 82 insertions(+), 53 deletions(-) diff --git a/crates/project-model/src/rustc_cfg.rs b/crates/project-model/src/rustc_cfg.rs index f8f9a5d5447..d6e041b3adf 100644 --- a/crates/project-model/src/rustc_cfg.rs +++ b/crates/project-model/src/rustc_cfg.rs @@ -2,15 +2,21 @@ use std::process::Command; +use anyhow::Context; use rustc_hash::FxHashMap; use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath, Sysroot}; +pub(crate) enum Config<'a> { + Cargo(&'a ManifestPath), + Explicit(&'a Sysroot), + Discover, +} + pub(crate) fn get( - cargo_toml: Option<&ManifestPath>, - sysroot: Option<&Sysroot>, target: Option<&str>, extra_env: &FxHashMap, + config: Config<'_>, ) -> Vec { let _p = profile::span("rustc_cfg::get"); let mut res = Vec::with_capacity(6 * 2 + 1); @@ -26,64 +32,68 @@ pub(crate) fn get( // Add miri cfg, which is useful for mir eval in stdlib res.push(CfgFlag::Atom("miri".into())); - match get_rust_cfgs(cargo_toml, sysroot, target, extra_env) { - Ok(rustc_cfgs) => { - tracing::debug!( - "rustc cfgs found: {:?}", - rustc_cfgs - .lines() - .map(|it| it.parse::().map(|it| it.to_string())) - .collect::>() - ); - res.extend(rustc_cfgs.lines().filter_map(|it| it.parse().ok())); + let rustc_cfgs = get_rust_cfgs(target, extra_env, config); + + let rustc_cfgs = match rustc_cfgs { + Ok(cfgs) => cfgs, + Err(e) => { + tracing::error!(?e, "failed to get rustc cfgs"); + return res; + } + }; + + let rustc_cfgs = + rustc_cfgs.lines().map(|it| it.parse::()).collect::, _>>(); + + match rustc_cfgs { + Ok(rustc_cfgs) => { + tracing::debug!(?rustc_cfgs, "rustc cfgs found"); + res.extend(rustc_cfgs); + } + Err(e) => { + tracing::error!(?e, "failed to get rustc cfgs") } - Err(e) => tracing::error!("failed to get rustc cfgs: {e:?}"), } res } fn get_rust_cfgs( - cargo_toml: Option<&ManifestPath>, - sysroot: Option<&Sysroot>, target: Option<&str>, extra_env: &FxHashMap, + config: Config<'_>, ) -> anyhow::Result { - if let Some(cargo_toml) = cargo_toml { - let mut cargo_config = Command::new(toolchain::cargo()); - cargo_config.envs(extra_env); - cargo_config - .current_dir(cargo_toml.parent()) - .args(["rustc", "-Z", "unstable-options", "--print", "cfg"]) - .env("RUSTC_BOOTSTRAP", "1"); - if let Some(target) = target { - cargo_config.args(["--target", target]); - } - match utf8_stdout(cargo_config) { - Ok(it) => return Ok(it), - Err(e) => tracing::debug!("{e:?}: falling back to querying rustc for cfgs"), - } - } + let mut cmd = match config { + Config::Cargo(cargo_toml) => { + let mut cmd = Command::new(toolchain::cargo()); + cmd.envs(extra_env); + cmd.current_dir(cargo_toml.parent()) + .args(["rustc", "-Z", "unstable-options", "--print", "cfg"]) + .env("RUSTC_BOOTSTRAP", "1"); + if let Some(target) = target { + cmd.args(["--target", target]); + } - let rustc = match sysroot { - Some(sysroot) => { - let rustc = sysroot.discover_rustc()?.into(); - tracing::debug!(?rustc, "using rustc from sysroot"); - rustc + return utf8_stdout(cmd).context("Unable to run `cargo rustc`"); } - None => { + Config::Explicit(sysroot) => { + let rustc: std::path::PathBuf = sysroot.discover_rustc()?.into(); + tracing::debug!(?rustc, "using explicit rustc from sysroot"); + Command::new(rustc) + } + Config::Discover => { let rustc = toolchain::rustc(); tracing::debug!(?rustc, "using rustc from env"); - rustc + Command::new(rustc) } }; - // using unstable cargo features failed, fall back to using plain rustc - let mut cmd = Command::new(rustc); cmd.envs(extra_env); cmd.args(["--print", "cfg", "-O"]); if let Some(target) = target { cmd.args(["--target", target]); } - utf8_stdout(cmd) + + let out = utf8_stdout(cmd).context("Unable to run `rustc`")?; + Ok(out) } diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 0eb88332908..6add6bf3b63 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -280,10 +280,9 @@ impl ProjectWorkspace { }); let rustc_cfg = rustc_cfg::get( - Some(&cargo_toml), - None, config.target.as_deref(), &config.extra_env, + rustc_cfg::Config::Cargo(cargo_toml), ); let cfg_overrides = config.cfg_overrides.clone(); @@ -335,11 +334,18 @@ impl ProjectWorkspace { } (None, None) => Err(None), }; - if let Ok(sysroot) = &sysroot { - tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); - } + let config = match &sysroot { + Ok(sysroot) => { + tracing::debug!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); + rustc_cfg::Config::Explicit(sysroot) + } + Err(_) => { + tracing::debug!("discovering sysroot"); + rustc_cfg::Config::Discover + } + }; - let rustc_cfg = rustc_cfg::get(None, sysroot.as_ref().ok(), target, extra_env); + let rustc_cfg = rustc_cfg::get(target, extra_env, config); ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg, toolchain } } @@ -361,10 +367,18 @@ impl ProjectWorkspace { } None => Err(None), }; - if let Ok(sysroot) = &sysroot { - tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); - } - let rustc_cfg = rustc_cfg::get(None, sysroot.as_ref().ok(), None, &Default::default()); + let rustc_config = match &sysroot { + Ok(sysroot) => { + tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); + rustc_cfg::Config::Explicit(sysroot) + } + Err(_) => { + tracing::info!("discovering sysroot"); + rustc_cfg::Config::Discover + } + }; + + let rustc_cfg = rustc_cfg::get(None, &FxHashMap::default(), rustc_config); Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg }) } @@ -758,9 +772,14 @@ fn project_json_to_crate_graph( let env = env.clone().into_iter().collect(); let target_cfgs = match target.as_deref() { - Some(target) => cfg_cache - .entry(target) - .or_insert_with(|| rustc_cfg::get(None, sysroot, Some(target), extra_env)), + Some(target) => cfg_cache.entry(target).or_insert_with(|| { + let rustc_cfg = match sysroot { + Some(sysroot) => rustc_cfg::Config::Explicit(sysroot), + None => rustc_cfg::Config::Discover, + }; + + rustc_cfg::get(Some(target), extra_env, rustc_cfg) + }), None => &rustc_cfg, }; From fad3823a20c453907ee311953fedc69b7ca11d8d Mon Sep 17 00:00:00 2001 From: David Barsky Date: Thu, 7 Sep 2023 15:19:04 -0400 Subject: [PATCH 104/250] rename `rustc_cfg::Config` to `rustc_cfg::RustcCfgConfig` and import --- crates/project-model/src/rustc_cfg.rs | 12 ++++++------ crates/project-model/src/workspace.rs | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/project-model/src/rustc_cfg.rs b/crates/project-model/src/rustc_cfg.rs index d6e041b3adf..f4af7b0d2f0 100644 --- a/crates/project-model/src/rustc_cfg.rs +++ b/crates/project-model/src/rustc_cfg.rs @@ -7,7 +7,7 @@ use rustc_hash::FxHashMap; use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath, Sysroot}; -pub(crate) enum Config<'a> { +pub(crate) enum RustcCfgConfig<'a> { Cargo(&'a ManifestPath), Explicit(&'a Sysroot), Discover, @@ -16,7 +16,7 @@ pub(crate) enum Config<'a> { pub(crate) fn get( target: Option<&str>, extra_env: &FxHashMap, - config: Config<'_>, + config: RustcCfgConfig<'_>, ) -> Vec { let _p = profile::span("rustc_cfg::get"); let mut res = Vec::with_capacity(6 * 2 + 1); @@ -61,10 +61,10 @@ pub(crate) fn get( fn get_rust_cfgs( target: Option<&str>, extra_env: &FxHashMap, - config: Config<'_>, + config: RustcCfgConfig<'_>, ) -> anyhow::Result { let mut cmd = match config { - Config::Cargo(cargo_toml) => { + RustcCfgConfig::Cargo(cargo_toml) => { let mut cmd = Command::new(toolchain::cargo()); cmd.envs(extra_env); cmd.current_dir(cargo_toml.parent()) @@ -76,12 +76,12 @@ fn get_rust_cfgs( return utf8_stdout(cmd).context("Unable to run `cargo rustc`"); } - Config::Explicit(sysroot) => { + RustcCfgConfig::Explicit(sysroot) => { let rustc: std::path::PathBuf = sysroot.discover_rustc()?.into(); tracing::debug!(?rustc, "using explicit rustc from sysroot"); Command::new(rustc) } - Config::Discover => { + RustcCfgConfig::Discover => { let rustc = toolchain::rustc(); tracing::debug!(?rustc, "using rustc from env"); Command::new(rustc) diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 6add6bf3b63..5d6c21d5972 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -21,7 +21,7 @@ use crate::{ cargo_workspace::{DepKind, PackageData, RustLibSource}, cfg_flag::CfgFlag, project_json::Crate, - rustc_cfg, + rustc_cfg::{self, RustcCfgConfig}, sysroot::SysrootCrate, target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath, Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts, @@ -282,7 +282,7 @@ impl ProjectWorkspace { let rustc_cfg = rustc_cfg::get( config.target.as_deref(), &config.extra_env, - rustc_cfg::Config::Cargo(cargo_toml), + RustcCfgConfig::Cargo(cargo_toml), ); let cfg_overrides = config.cfg_overrides.clone(); @@ -337,11 +337,11 @@ impl ProjectWorkspace { let config = match &sysroot { Ok(sysroot) => { tracing::debug!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); - rustc_cfg::Config::Explicit(sysroot) + RustcCfgConfig::Explicit(sysroot) } Err(_) => { tracing::debug!("discovering sysroot"); - rustc_cfg::Config::Discover + RustcCfgConfig::Discover } }; @@ -370,11 +370,11 @@ impl ProjectWorkspace { let rustc_config = match &sysroot { Ok(sysroot) => { tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); - rustc_cfg::Config::Explicit(sysroot) + RustcCfgConfig::Explicit(sysroot) } Err(_) => { tracing::info!("discovering sysroot"); - rustc_cfg::Config::Discover + RustcCfgConfig::Discover } }; @@ -774,8 +774,8 @@ fn project_json_to_crate_graph( let target_cfgs = match target.as_deref() { Some(target) => cfg_cache.entry(target).or_insert_with(|| { let rustc_cfg = match sysroot { - Some(sysroot) => rustc_cfg::Config::Explicit(sysroot), - None => rustc_cfg::Config::Discover, + Some(sysroot) => RustcCfgConfig::Explicit(sysroot), + None => RustcCfgConfig::Discover, }; rustc_cfg::get(Some(target), extra_env, rustc_cfg) From 912b22fa0795b56aab514ed2801fb1a7b40f7704 Mon Sep 17 00:00:00 2001 From: David Barsky Date: Thu, 7 Sep 2023 15:30:11 -0400 Subject: [PATCH 105/250] add doc comment to `rustc_cfg::RustcCfgConfig` --- crates/project-model/src/rustc_cfg.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/project-model/src/rustc_cfg.rs b/crates/project-model/src/rustc_cfg.rs index f4af7b0d2f0..905adff4e5c 100644 --- a/crates/project-model/src/rustc_cfg.rs +++ b/crates/project-model/src/rustc_cfg.rs @@ -7,6 +7,14 @@ use rustc_hash::FxHashMap; use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath, Sysroot}; +/// Determines how `rustc --print cfg` is discovered and invoked. +/// +/// There options are supported: +/// - [`RustcCfgConfig::Cargo`], which relies on `cargo rustc --print cfg` +/// and `RUSTC_BOOTSTRAP`. +/// - [`RustcCfgConfig::Explicit`], which uses an explicit path to the `rustc` +/// binary in the sysroot. +/// - [`RustcCfgConfig::Discover`], which uses [`toolchain::rustc`]. pub(crate) enum RustcCfgConfig<'a> { Cargo(&'a ManifestPath), Explicit(&'a Sysroot), From 9762f764aeecd532628ecad91c5e3a49c0a01380 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Tue, 5 Sep 2023 00:29:57 +0200 Subject: [PATCH 106/250] Add assist `into_to_qualified_from` This assist converts an `.into()` call into an explicit fully qualified from call. --- .../src/handlers/into_to_qualified_from.rs | 205 ++++++++++++++++++ crates/ide-assists/src/lib.rs | 2 + crates/ide-assists/src/tests/generated.rs | 34 +++ 3 files changed, 241 insertions(+) create mode 100644 crates/ide-assists/src/handlers/into_to_qualified_from.rs diff --git a/crates/ide-assists/src/handlers/into_to_qualified_from.rs b/crates/ide-assists/src/handlers/into_to_qualified_from.rs new file mode 100644 index 00000000000..663df266b6f --- /dev/null +++ b/crates/ide-assists/src/handlers/into_to_qualified_from.rs @@ -0,0 +1,205 @@ +use hir::{AsAssocItem, HirDisplay}; +use ide_db::{ + assists::{AssistId, AssistKind}, + famous_defs::FamousDefs, +}; +use syntax::{ast, AstNode}; + +use crate::assist_context::{AssistContext, Assists}; + +// Assist: into_to_qualified_from +// +// Convert an `into` method call to a fully qualified `from` call. +// +// ``` +// //- minicore: from +// struct B; +// impl From for B { +// fn from(a: i32) -> Self { +// B +// } +// } +// +// fn main() -> () { +// let a = 3; +// let b: B = a.in$0to(); +// } +// ``` +// -> +// ``` +// struct B; +// impl From for B { +// fn from(a: i32) -> Self { +// B +// } +// } +// +// fn main() -> () { +// let a = 3; +// let b: B = B::from(a); +// } +// ``` +pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?; + let nameref = method_call.name_ref()?; + let receiver = method_call.receiver()?; + let db = ctx.db(); + let sema = &ctx.sema; + let fnc = sema.resolve_method_call(&method_call)?; + let scope = sema.scope(method_call.syntax())?; + // Check if the method call refers to Into trait. + if fnc.as_assoc_item(db)?.containing_trait_impl(db)? + == FamousDefs(sema, scope.krate()).core_convert_Into()? + { + let type_call = sema.type_of_expr(&method_call.clone().into())?; + let type_call_disp = + type_call.adjusted().display_source_code(db, scope.module().into(), true).ok()?; + + acc.add( + AssistId("into_to_qualified_from", AssistKind::Generate), + "Convert `into` to fully qualified `from`", + nameref.syntax().text_range(), + |edit| { + edit.replace( + method_call.syntax().text_range(), + format!("{}::from({})", type_call_disp, receiver), + ); + }, + ); + } + + Some(()) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_assist; + + use super::into_to_qualified_from; + + #[test] + fn two_types_in_same_mod() { + check_assist( + into_to_qualified_from, + r#" +//- minicore: from +struct A; +struct B; +impl From for B { + fn from(a: A) -> Self { + B + } +} + +fn main() -> () { + let a: A = A; + let b: B = a.in$0to(); +}"#, + r#" +struct A; +struct B; +impl From for B { + fn from(a: A) -> Self { + B + } +} + +fn main() -> () { + let a: A = A; + let b: B = B::from(a); +}"#, + ) + } + + #[test] + fn fromed_in_child_mod_imported() { + check_assist( + into_to_qualified_from, + r#" +//- minicore: from +use C::B; + +struct A; + +mod C { + use crate::A; + + pub(super) struct B; + impl From for B { + fn from(a: A) -> Self { + B + } + } +} + +fn main() -> () { + let a: A = A; + let b: B = a.in$0to(); +}"#, + r#" +use C::B; + +struct A; + +mod C { + use crate::A; + + pub(super) struct B; + impl From for B { + fn from(a: A) -> Self { + B + } + } +} + +fn main() -> () { + let a: A = A; + let b: B = B::from(a); +}"#, + ) + } + + #[test] + fn fromed_in_child_mod_not_imported() { + check_assist( + into_to_qualified_from, + r#" +//- minicore: from +struct A; + +mod C { + use crate::A; + + pub(super) struct B; + impl From for B { + fn from(a: A) -> Self { + B + } + } +} + +fn main() -> () { + let a: A = A; + let b: C::B = a.in$0to(); +}"#, + r#" +struct A; + +mod C { + use crate::A; + + pub(super) struct B; + impl From for B { + fn from(a: A) -> Self { + B + } + } +} + +fn main() -> () { + let a: A = A; + let b: C::B = C::B::from(a); +}"#, + ) + } +} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 2ebb5ef9b19..7136bdab210 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -211,6 +211,7 @@ mod handlers { mod unwrap_result_return_type; mod unqualify_method_call; mod wrap_return_type_in_result; + mod into_to_qualified_from; pub(crate) fn all() -> &'static [Handler] { &[ @@ -274,6 +275,7 @@ mod handlers { inline_local_variable::inline_local_variable, inline_type_alias::inline_type_alias, inline_type_alias::inline_type_alias_uses, + into_to_qualified_from::into_to_qualified_from, introduce_named_generic::introduce_named_generic, introduce_named_lifetime::introduce_named_lifetime, invert_if::invert_if, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 6eadc3dbcbc..cd3bb561cda 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -1754,6 +1754,40 @@ fn foo() { ) } +#[test] +fn doctest_into_to_qualified_from() { + check_doc_test( + "into_to_qualified_from", + r#####" +//- minicore: from +struct B; +impl From for B { + fn from(a: i32) -> Self { + B + } +} + +fn main() -> () { + let a = 3; + let b: B = a.in$0to(); +} +"#####, + r#####" +struct B; +impl From for B { + fn from(a: i32) -> Self { + B + } +} + +fn main() -> () { + let a = 3; + let b: B = B::from(a); +} +"#####, + ) +} + #[test] fn doctest_introduce_named_generic() { check_doc_test( From 506a477acf98a64887fa99d4b821235be0604449 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Fri, 8 Sep 2023 01:25:25 +0200 Subject: [PATCH 107/250] Use cargo name as the CARGO_CRATE_NAME instead of package name --- crates/project-model/src/workspace.rs | 6 +++++- .../test_data/output/cargo_hello_world_project_model.txt | 4 ++-- ...o_hello_world_project_model_with_selective_overrides.txt | 4 ++-- ...go_hello_world_project_model_with_wildcard_overrides.txt | 4 ++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 13463e9f72e..8cb0e65be1d 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -2,7 +2,7 @@ //! metadata` or `rust-project.json`) into representation stored in the salsa //! database -- `CrateGraph`. -use std::{collections::VecDeque, fmt, fs, process::Command, sync}; +use std::{collections::VecDeque, fmt, fs, process::Command, str::FromStr, sync}; use anyhow::{format_err, Context}; use base_db::{ @@ -1228,6 +1228,10 @@ fn add_target_crate_root( let mut env = Env::default(); inject_cargo_env(pkg, &mut env); + if let Ok(cname) = String::from_str(cargo_name) { + // CARGO_CRATE_NAME is the name of the Cargo target with - converted to _, such as the name of the library, binary, example, integration test, or benchmark. + env.set("CARGO_CRATE_NAME", cname.replace("-", "_")); + } if let Some(envs) = build_data.map(|it| &it.envs) { for (k, v) in envs { diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt index e595cd82729..447c7e5c869 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt @@ -162,7 +162,7 @@ "CARGO_MANIFEST_DIR": "$ROOT$hello-world", "CARGO_PKG_VERSION": "0.1.0", "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", + "CARGO_CRATE_NAME": "an_example", "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_HOMEPAGE": "", "CARGO_PKG_DESCRIPTION": "", @@ -232,7 +232,7 @@ "CARGO_MANIFEST_DIR": "$ROOT$hello-world", "CARGO_PKG_VERSION": "0.1.0", "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", + "CARGO_CRATE_NAME": "it", "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_HOMEPAGE": "", "CARGO_PKG_DESCRIPTION": "", diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt index e595cd82729..447c7e5c869 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt @@ -162,7 +162,7 @@ "CARGO_MANIFEST_DIR": "$ROOT$hello-world", "CARGO_PKG_VERSION": "0.1.0", "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", + "CARGO_CRATE_NAME": "an_example", "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_HOMEPAGE": "", "CARGO_PKG_DESCRIPTION": "", @@ -232,7 +232,7 @@ "CARGO_MANIFEST_DIR": "$ROOT$hello-world", "CARGO_PKG_VERSION": "0.1.0", "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", + "CARGO_CRATE_NAME": "it", "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_HOMEPAGE": "", "CARGO_PKG_DESCRIPTION": "", diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt index f10c55d0462..2e8ece82a10 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt @@ -159,7 +159,7 @@ "CARGO_MANIFEST_DIR": "$ROOT$hello-world", "CARGO_PKG_VERSION": "0.1.0", "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", + "CARGO_CRATE_NAME": "an_example", "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_HOMEPAGE": "", "CARGO_PKG_DESCRIPTION": "", @@ -228,7 +228,7 @@ "CARGO_MANIFEST_DIR": "$ROOT$hello-world", "CARGO_PKG_VERSION": "0.1.0", "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", + "CARGO_CRATE_NAME": "it", "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_HOMEPAGE": "", "CARGO_PKG_DESCRIPTION": "", From 35605299acab0c2715fa772bfdf2f3d8cfdc975d Mon Sep 17 00:00:00 2001 From: Keita Nonaka Date: Thu, 7 Sep 2023 22:18:21 -0700 Subject: [PATCH 108/250] ci: actions/checkout@v3 to actions/checkout@v4 --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/dependencies.yml | 4 ++-- src/ci/github-actions/ci.yml | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3680136d89f..6ba73192c92 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,7 @@ jobs: - name: disable git crlf conversion run: git config --global core.autocrlf false - name: checkout the source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 - name: configure the PR in which the error message will be posted @@ -435,7 +435,7 @@ jobs: - name: disable git crlf conversion run: git config --global core.autocrlf false - name: checkout the source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 - name: configure the PR in which the error message will be posted @@ -555,7 +555,7 @@ jobs: - name: disable git crlf conversion run: git config --global core.autocrlf false - name: checkout the source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 - name: configure the PR in which the error message will be posted @@ -662,7 +662,7 @@ jobs: if: "github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'rust-lang-ci/rust'" steps: - name: checkout the source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 - name: publish toolstate diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index 26d2ba636f3..97ed891c491 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -50,7 +50,7 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout the source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive - name: install the bootstrap toolchain @@ -87,7 +87,7 @@ jobs: pull-requests: write steps: - name: checkout the source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: download Cargo.lock from update job uses: actions/download-artifact@v3 diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 89b82d59d31..9a286e5aa50 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -114,7 +114,7 @@ x--expand-yaml-anchors--remove: run: git config --global core.autocrlf false - name: checkout the source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 @@ -707,7 +707,7 @@ jobs: if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'rust-lang-ci/rust' steps: - name: checkout the source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 From ca6ddd8ea3f93bc6c32ec732426691e917175a2f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 29 Aug 2023 10:08:34 +0200 Subject: [PATCH 109/250] Enable `rust_analyzer` for cfgs when code is being analyzed by rust-analyzer --- crates/ide-db/src/search.rs | 6 +++-- crates/project-model/src/workspace.rs | 33 ++++++++++++++++----------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 7e00d368652..2ffce3c7513 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -221,7 +221,6 @@ impl Definition { } // def is crate root - // FIXME: We don't do searches for crates currently, as a crate does not actually have a single name if let &Definition::Module(module) = self { if module.is_crate_root() { return SearchScope::reverse_dependencies(db, module.krate()); @@ -393,7 +392,10 @@ impl<'a> FindUsages<'a> { let name = match self.def { // special case crate modules as these do not have a proper name Definition::Module(module) if module.is_crate_root() => { - // FIXME: This assumes the crate name is always equal to its display name when it really isn't + // FIXME: This assumes the crate name is always equal to its display name when it + // really isn't + // we should instead look at the dependency edge name and recursively search our way + // up the ancestors module .krate() .display_name(self.sema.db) diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 8cb0e65be1d..6e7836f9130 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -2,7 +2,7 @@ //! metadata` or `rust-project.json`) into representation stored in the salsa //! database -- `CrateGraph`. -use std::{collections::VecDeque, fmt, fs, process::Command, str::FromStr, sync}; +use std::{collections::VecDeque, fmt, fs, iter, process::Command, str::FromStr, sync}; use anyhow::{format_err, Context}; use base_db::{ @@ -730,6 +730,7 @@ fn project_json_to_crate_graph( ) }); + let r_a_cfg_flag = CfgFlag::Atom("rust_analyzer".to_owned()); let mut cfg_cache: FxHashMap<&str, Vec> = FxHashMap::default(); let crates: FxHashMap = project .crates() @@ -765,7 +766,12 @@ fn project_json_to_crate_graph( *edition, display_name.clone(), version.clone(), - target_cfgs.iter().chain(cfg.iter()).cloned().collect(), + target_cfgs + .iter() + .chain(cfg.iter()) + .chain(iter::once(&r_a_cfg_flag)) + .cloned() + .collect(), None, env, *is_proc_macro, @@ -820,7 +826,7 @@ fn cargo_to_crate_graph( sysroot: Option<&Sysroot>, rustc_cfg: Vec, override_cfg: &CfgOverrides, - // Don't compute cfg and use this if present + // Don't compute cfg and use this if present, only used for the sysroot experiment hack forced_cfg: Option, build_scripts: &WorkspaceBuildScripts, target_layout: TargetLayoutLoadResult, @@ -842,12 +848,7 @@ fn cargo_to_crate_graph( None => (SysrootPublicDeps::default(), None), }; - let cfg_options = { - let mut cfg_options = CfgOptions::default(); - cfg_options.extend(rustc_cfg); - cfg_options.insert_atom("debug_assertions".into()); - cfg_options - }; + let cfg_options = create_cfg_options(rustc_cfg); // Mapping of a package to its library target let mut pkg_to_lib_crate = FxHashMap::default(); @@ -1029,8 +1030,7 @@ fn detached_files_to_crate_graph( None => (SysrootPublicDeps::default(), None), }; - let mut cfg_options = CfgOptions::default(); - cfg_options.extend(rustc_cfg); + let cfg_options = create_cfg_options(rustc_cfg); for detached_file in detached_files { let file_id = match load(detached_file) { @@ -1295,8 +1295,7 @@ fn sysroot_to_crate_graph( channel: Option, ) -> (SysrootPublicDeps, Option) { let _p = profile::span("sysroot_to_crate_graph"); - let mut cfg_options = CfgOptions::default(); - cfg_options.extend(rustc_cfg.clone()); + let cfg_options = create_cfg_options(rustc_cfg.clone()); let sysroot_crates: FxHashMap = match &sysroot.hack_cargo_workspace { Some(cargo) => handle_hack_cargo_workspace( load, @@ -1475,3 +1474,11 @@ fn inject_cargo_env(package: &PackageData, env: &mut Env) { env.set("CARGO_PKG_LICENSE_FILE", String::new()); } + +fn create_cfg_options(rustc_cfg: Vec) -> CfgOptions { + let mut cfg_options = CfgOptions::default(); + cfg_options.extend(rustc_cfg); + cfg_options.insert_atom("debug_assertions".into()); + cfg_options.insert_atom("rust_analyzer".into()); + cfg_options +} From 853f8a21f7e403201617dcf93c5340859b35ac1b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 29 Aug 2023 10:57:24 +0200 Subject: [PATCH 110/250] Fix cfg completions not working --- crates/base-db/src/fixture.rs | 10 ++-- crates/cfg/src/lib.rs | 26 +++++++++ .../src/completions/attribute/cfg.rs | 57 +++++++++++-------- crates/ide-completion/src/tests/attribute.rs | 38 +++++++++++-- 4 files changed, 98 insertions(+), 33 deletions(-) diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs index aaac0fc3790..3f5ccb621c7 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/base-db/src/fixture.rs @@ -179,8 +179,8 @@ impl ChangeFixture { meta.edition, Some(crate_name.clone().into()), version, - meta.cfg, - Default::default(), + meta.cfg.clone(), + Some(meta.cfg), meta.env, false, origin, @@ -200,7 +200,7 @@ impl ChangeFixture { } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { assert!(default_crate_root.is_none()); default_crate_root = Some(file_id); - default_cfg = meta.cfg; + default_cfg.extend(meta.cfg.into_iter()); default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned()))); default_target_data_layout = meta.target_data_layout; } @@ -220,8 +220,8 @@ impl ChangeFixture { Edition::CURRENT, Some(CrateName::new("test").unwrap().into()), None, - default_cfg, - Default::default(), + default_cfg.clone(), + Some(default_cfg), default_env, false, CrateOrigin::Local { repo: None, name: None }, diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 183b9b7d278..0aeb0b05052 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs @@ -86,6 +86,32 @@ impl CfgOptions { } } +impl Extend for CfgOptions { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each(|cfg_flag| _ = self.enabled.insert(cfg_flag)); + } +} + +impl IntoIterator for CfgOptions { + type Item = as IntoIterator>::Item; + + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + as IntoIterator>::into_iter(self.enabled) + } +} + +impl<'a> IntoIterator for &'a CfgOptions { + type Item = <&'a FxHashSet as IntoIterator>::Item; + + type IntoIter = <&'a FxHashSet as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + <&FxHashSet as IntoIterator>::into_iter(&self.enabled) + } +} + #[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct CfgDiff { // Invariants: No duplicates, no atom that's both in `enable` and `disable`. diff --git a/crates/ide-completion/src/completions/attribute/cfg.rs b/crates/ide-completion/src/completions/attribute/cfg.rs index 62bdb6ee688..87a286778e6 100644 --- a/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/crates/ide-completion/src/completions/attribute/cfg.rs @@ -1,10 +1,8 @@ //! Completion for cfg -use std::iter; - use ide_db::SymbolKind; use itertools::Itertools; -use syntax::SyntaxKind; +use syntax::{algo, ast::Ident, AstToken, Direction, NodeOrToken, SyntaxKind}; use crate::{completions::Completions, context::CompletionContext, CompletionItem}; @@ -15,31 +13,44 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { acc.add(completion.build(ctx.db)); }; - let previous = iter::successors(ctx.original_token.prev_token(), |t| { - (matches!(t.kind(), SyntaxKind::EQ) || t.kind().is_trivia()) - .then(|| t.prev_token()) - .flatten() - }) - .find(|t| matches!(t.kind(), SyntaxKind::IDENT)); + // FIXME: Move this into context/analysis.rs + let previous = ctx + .original_token + .prev_token() + .and_then(|it| { + if matches!(it.kind(), SyntaxKind::EQ) { + Some(it.into()) + } else { + algo::non_trivia_sibling(it.into(), Direction::Prev) + } + }) + .filter(|t| matches!(t.kind(), SyntaxKind::EQ)) + .and_then(|it| algo::non_trivia_sibling(it.prev_sibling_or_token()?, Direction::Prev)) + .map(|it| match it { + NodeOrToken::Node(_) => None, + NodeOrToken::Token(t) => Ident::cast(t), + }); + match previous { + Some(None) => (), + Some(Some(p)) => match p.text() { + "target_arch" => KNOWN_ARCH.iter().copied().for_each(add_completion), + "target_env" => KNOWN_ENV.iter().copied().for_each(add_completion), + "target_os" => KNOWN_OS.iter().copied().for_each(add_completion), + "target_vendor" => KNOWN_VENDOR.iter().copied().for_each(add_completion), + "target_endian" => ["little", "big"].into_iter().for_each(add_completion), + name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| { + let insert_text = format!(r#""{s}""#); + let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); + item.insert_text(insert_text); - match previous.as_ref().map(|p| p.text()) { - Some("target_arch") => KNOWN_ARCH.iter().copied().for_each(add_completion), - Some("target_env") => KNOWN_ENV.iter().copied().for_each(add_completion), - Some("target_os") => KNOWN_OS.iter().copied().for_each(add_completion), - Some("target_vendor") => KNOWN_VENDOR.iter().copied().for_each(add_completion), - Some("target_endian") => ["little", "big"].into_iter().for_each(add_completion), - Some(name) => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| { - let insert_text = format!(r#""{s}""#); - let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); - item.insert_text(insert_text); - - acc.add(item.build(ctx.db)); - }), + acc.add(item.build(ctx.db)); + }), + }, None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| { let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); acc.add(item.build(ctx.db)); }), - }; + } } const KNOWN_ARCH: [&str; 20] = [ diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs index 1aaf3958726..d8c134c533b 100644 --- a/crates/ide-completion/src/tests/attribute.rs +++ b/crates/ide-completion/src/tests/attribute.rs @@ -66,11 +66,6 @@ struct Foo; ) } -#[test] -fn inside_nested_attr() { - check(r#"#[cfg($0)]"#, expect![[]]) -} - #[test] fn with_existing_attr() { check( @@ -635,6 +630,32 @@ struct Foo; mod cfg { use super::*; + #[test] + fn inside_cfg() { + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0)] +"#, + expect![[r#" + ba dbg + ba opt_level + ba test + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg(b$0)] +"#, + expect![[r#" + ba dbg + ba opt_level + ba test + "#]], + ); + } + #[test] fn cfg_target_endian() { check( @@ -644,6 +665,13 @@ mod cfg { ba little "#]], ); + check( + r#"#[cfg(target_endian = b$0"#, + expect![[r#" + ba big + ba little + "#]], + ); } } From e9e2c1ae75e7296435c9835a1078c3e14187832a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 29 Aug 2023 10:58:28 +0200 Subject: [PATCH 111/250] Update project-model test outputs --- .../cargo_hello_world_project_model.txt | 6 +++ ...project_model_with_selective_overrides.txt | 6 +++ ..._project_model_with_wildcard_overrides.txt | 6 +++ ...rust_project_hello_world_project_model.txt | 54 +++++++++++++++---- 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt index 447c7e5c869..b5e49371155 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt @@ -18,6 +18,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -81,6 +82,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -151,6 +153,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -221,6 +224,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -293,6 +297,7 @@ "debug_assertions", "feature=default", "feature=std", + "rust_analyzer", ], ), potential_cfg_options: Some( @@ -306,6 +311,7 @@ "feature=rustc-dep-of-std", "feature=std", "feature=use_std", + "rust_analyzer", ], ), ), diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt index 447c7e5c869..b5e49371155 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt @@ -18,6 +18,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -81,6 +82,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -151,6 +153,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -221,6 +224,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -293,6 +297,7 @@ "debug_assertions", "feature=default", "feature=std", + "rust_analyzer", ], ), potential_cfg_options: Some( @@ -306,6 +311,7 @@ "feature=rustc-dep-of-std", "feature=std", "feature=use_std", + "rust_analyzer", ], ), ), diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt index 2e8ece82a10..050791d64c3 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt @@ -18,6 +18,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", ], ), potential_cfg_options: None, @@ -80,6 +81,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", ], ), potential_cfg_options: None, @@ -149,6 +151,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", ], ), potential_cfg_options: None, @@ -218,6 +221,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", ], ), potential_cfg_options: None, @@ -289,6 +293,7 @@ "debug_assertions", "feature=default", "feature=std", + "rust_analyzer", ], ), potential_cfg_options: Some( @@ -302,6 +307,7 @@ "feature=rustc-dep-of-std", "feature=std", "feature=use_std", + "rust_analyzer", ], ), ), diff --git a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt index fb3f5933b17..24803e7d9ac 100644 --- a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt +++ b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt @@ -14,7 +14,10 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + "rust_analyzer", + ], ), potential_cfg_options: None, env: Env { @@ -53,7 +56,10 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + "rust_analyzer", + ], ), potential_cfg_options: None, env: Env { @@ -84,7 +90,10 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + "rust_analyzer", + ], ), potential_cfg_options: None, env: Env { @@ -115,7 +124,10 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + "rust_analyzer", + ], ), potential_cfg_options: None, env: Env { @@ -146,7 +158,10 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + "rust_analyzer", + ], ), potential_cfg_options: None, env: Env { @@ -192,7 +207,10 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + "rust_analyzer", + ], ), potential_cfg_options: None, env: Env { @@ -223,7 +241,10 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + "rust_analyzer", + ], ), potential_cfg_options: None, env: Env { @@ -311,7 +332,10 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + "rust_analyzer", + ], ), potential_cfg_options: None, env: Env { @@ -342,7 +366,10 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + "rust_analyzer", + ], ), potential_cfg_options: None, env: Env { @@ -373,7 +400,10 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + "rust_analyzer", + ], ), potential_cfg_options: None, env: Env { @@ -404,7 +434,9 @@ }, ), cfg_options: CfgOptions( - [], + [ + "rust_analyzer", + ], ), potential_cfg_options: None, env: Env { From fddef42e92f69b04c2b39d2accbe74a86ae4cc22 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 8 Sep 2023 11:02:56 +0200 Subject: [PATCH 112/250] Only set rust-analyzer cfg for workspace members --- crates/project-model/src/workspace.rs | 7 +++++-- .../output/cargo_hello_world_project_model.txt | 2 -- ...lo_world_project_model_with_selective_overrides.txt | 2 -- ...llo_world_project_model_with_wildcard_overrides.txt | 2 -- .../output/rust_project_hello_world_project_model.txt | 10 ---------- 5 files changed, 5 insertions(+), 18 deletions(-) diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 6e7836f9130..0904ef080d4 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -867,6 +867,9 @@ fn cargo_to_crate_graph( if cargo[pkg].is_local { cfg_options.insert_atom("test".into()); } + if cargo[pkg].is_member { + cfg_options.insert_atom("rust_analyzer".into()); + } if !override_cfg.global.is_empty() { cfg_options.apply_diff(override_cfg.global.clone()); @@ -1030,7 +1033,8 @@ fn detached_files_to_crate_graph( None => (SysrootPublicDeps::default(), None), }; - let cfg_options = create_cfg_options(rustc_cfg); + let mut cfg_options = create_cfg_options(rustc_cfg); + cfg_options.insert_atom("rust_analyzer".into()); for detached_file in detached_files { let file_id = match load(detached_file) { @@ -1479,6 +1483,5 @@ fn create_cfg_options(rustc_cfg: Vec) -> CfgOptions { let mut cfg_options = CfgOptions::default(); cfg_options.extend(rustc_cfg); cfg_options.insert_atom("debug_assertions".into()); - cfg_options.insert_atom("rust_analyzer".into()); cfg_options } diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt index b5e49371155..727d39a3077 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt @@ -297,7 +297,6 @@ "debug_assertions", "feature=default", "feature=std", - "rust_analyzer", ], ), potential_cfg_options: Some( @@ -311,7 +310,6 @@ "feature=rustc-dep-of-std", "feature=std", "feature=use_std", - "rust_analyzer", ], ), ), diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt index b5e49371155..727d39a3077 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt @@ -297,7 +297,6 @@ "debug_assertions", "feature=default", "feature=std", - "rust_analyzer", ], ), potential_cfg_options: Some( @@ -311,7 +310,6 @@ "feature=rustc-dep-of-std", "feature=std", "feature=use_std", - "rust_analyzer", ], ), ), diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt index 050791d64c3..89728babd82 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt @@ -293,7 +293,6 @@ "debug_assertions", "feature=default", "feature=std", - "rust_analyzer", ], ), potential_cfg_options: Some( @@ -307,7 +306,6 @@ "feature=rustc-dep-of-std", "feature=std", "feature=use_std", - "rust_analyzer", ], ), ), diff --git a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt index 24803e7d9ac..b7bf6cb2774 100644 --- a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt +++ b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt @@ -16,7 +16,6 @@ cfg_options: CfgOptions( [ "debug_assertions", - "rust_analyzer", ], ), potential_cfg_options: None, @@ -58,7 +57,6 @@ cfg_options: CfgOptions( [ "debug_assertions", - "rust_analyzer", ], ), potential_cfg_options: None, @@ -92,7 +90,6 @@ cfg_options: CfgOptions( [ "debug_assertions", - "rust_analyzer", ], ), potential_cfg_options: None, @@ -126,7 +123,6 @@ cfg_options: CfgOptions( [ "debug_assertions", - "rust_analyzer", ], ), potential_cfg_options: None, @@ -160,7 +156,6 @@ cfg_options: CfgOptions( [ "debug_assertions", - "rust_analyzer", ], ), potential_cfg_options: None, @@ -209,7 +204,6 @@ cfg_options: CfgOptions( [ "debug_assertions", - "rust_analyzer", ], ), potential_cfg_options: None, @@ -243,7 +237,6 @@ cfg_options: CfgOptions( [ "debug_assertions", - "rust_analyzer", ], ), potential_cfg_options: None, @@ -334,7 +327,6 @@ cfg_options: CfgOptions( [ "debug_assertions", - "rust_analyzer", ], ), potential_cfg_options: None, @@ -368,7 +360,6 @@ cfg_options: CfgOptions( [ "debug_assertions", - "rust_analyzer", ], ), potential_cfg_options: None, @@ -402,7 +393,6 @@ cfg_options: CfgOptions( [ "debug_assertions", - "rust_analyzer", ], ), potential_cfg_options: None, From 9708a29e57346c77c5dba756e44ab2c0d7fc9adc Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 8 Sep 2023 12:39:41 +0330 Subject: [PATCH 113/250] Intern projections in mir place --- crates/hir-ty/src/mir.rs | 137 ++++++++++++---- crates/hir-ty/src/mir/borrowck.rs | 12 +- crates/hir-ty/src/mir/eval.rs | 31 ++-- crates/hir-ty/src/mir/lower.rs | 154 ++++++++++-------- crates/hir-ty/src/mir/lower/as_place.rs | 16 +- .../hir-ty/src/mir/lower/pattern_matching.rs | 58 ++++--- crates/hir-ty/src/mir/pretty.rs | 2 +- 7 files changed, 253 insertions(+), 157 deletions(-) diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index 9be083d0117..3ee141b5536 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -1,6 +1,6 @@ //! MIR definitions and implementation -use std::{fmt::Display, iter}; +use std::{collections::hash_map::Entry, fmt::Display, iter}; use crate::{ consteval::usize_const, @@ -37,6 +37,7 @@ pub use monomorphization::{ monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query, monomorphized_mir_body_query, monomorphized_mir_body_recover, }; +use rustc_hash::FxHashMap; use smallvec::{smallvec, SmallVec}; use stdx::{impl_from, never}; use triomphe::Arc; @@ -223,35 +224,93 @@ impl ProjectionElem { type PlaceElem = ProjectionElem; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ProjectionId(u32); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProjectionStore { + id_to_proj: FxHashMap>, + proj_to_id: FxHashMap, ProjectionId>, +} + +impl Default for ProjectionStore { + fn default() -> Self { + let mut this = Self { id_to_proj: Default::default(), proj_to_id: Default::default() }; + // Ensure that [] will get the id 0 which is used in `ProjectionId::Empty` + this.intern(Box::new([])); + this + } +} + +impl ProjectionStore { + fn shrink_to_fit(&mut self) { + self.id_to_proj.shrink_to_fit(); + self.proj_to_id.shrink_to_fit(); + } + + fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option { + self.proj_to_id.get(projection).copied() + } + + fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId { + let new_id = ProjectionId(self.proj_to_id.len() as u32); + match self.proj_to_id.entry(projection) { + Entry::Occupied(id) => *id.get(), + Entry::Vacant(e) => { + let key_clone = e.key().clone(); + e.insert(new_id); + self.id_to_proj.insert(new_id, key_clone); + new_id + } + } + } +} + +impl ProjectionId { + const EMPTY: ProjectionId = ProjectionId(0); + + fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] { + store.id_to_proj.get(&self).unwrap() + } + + fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId { + let mut current = self.lookup(store).to_vec(); + current.push(projection); + store.intern(current.into()) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Place { pub local: LocalId, - pub projection: Box<[PlaceElem]>, + pub projection: ProjectionId, } impl Place { - fn is_parent(&self, child: &Place) -> bool { - self.local == child.local && child.projection.starts_with(&self.projection) + fn is_parent(&self, child: &Place, store: &ProjectionStore) -> bool { + self.local == child.local + && child.projection.lookup(store).starts_with(&self.projection.lookup(store)) } /// The place itself is not included - fn iterate_over_parents(&self) -> impl Iterator + '_ { - (0..self.projection.len()) - .map(|x| &self.projection[0..x]) - .map(|x| Place { local: self.local, projection: x.to_vec().into() }) + fn iterate_over_parents<'a>( + &'a self, + store: &'a ProjectionStore, + ) -> impl Iterator + 'a { + let projection = self.projection.lookup(store); + (0..projection.len()).map(|x| &projection[0..x]).filter_map(move |x| { + Some(Place { local: self.local, projection: store.intern_if_exist(x)? }) + }) } - fn project(&self, projection: PlaceElem) -> Place { - Place { - local: self.local, - projection: self.projection.iter().cloned().chain([projection]).collect(), - } + fn project(&self, projection: PlaceElem, store: &mut ProjectionStore) -> Place { + Place { local: self.local, projection: self.projection.project(projection, store) } } } impl From for Place { fn from(local: LocalId) -> Self { - Self { local, projection: vec![].into() } + Self { local, projection: ProjectionId::EMPTY } } } @@ -997,6 +1056,7 @@ pub struct BasicBlock { #[derive(Debug, Clone, PartialEq, Eq)] pub struct MirBody { + pub projection_store: ProjectionStore, pub basic_blocks: Arena, pub locals: Arena, pub start_block: BasicBlockId, @@ -1009,11 +1069,15 @@ pub struct MirBody { } impl MirBody { - fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) { - fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) { + fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) { + fn for_operand( + op: &mut Operand, + f: &mut impl FnMut(&mut Place, &mut ProjectionStore), + store: &mut ProjectionStore, + ) { match op { Operand::Copy(p) | Operand::Move(p) => { - f(p); + f(p, store); } Operand::Constant(_) | Operand::Static(_) => (), } @@ -1022,30 +1086,30 @@ impl MirBody { for statement in &mut block.statements { match &mut statement.kind { StatementKind::Assign(p, r) => { - f(p); + f(p, &mut self.projection_store); match r { Rvalue::ShallowInitBoxWithAlloc(_) => (), Rvalue::ShallowInitBox(o, _) | Rvalue::UnaryOp(_, o) | Rvalue::Cast(_, o, _) | Rvalue::Repeat(o, _) - | Rvalue::Use(o) => for_operand(o, &mut f), + | Rvalue::Use(o) => for_operand(o, &mut f, &mut self.projection_store), Rvalue::CopyForDeref(p) | Rvalue::Discriminant(p) | Rvalue::Len(p) - | Rvalue::Ref(_, p) => f(p), + | Rvalue::Ref(_, p) => f(p, &mut self.projection_store), Rvalue::CheckedBinaryOp(_, o1, o2) => { - for_operand(o1, &mut f); - for_operand(o2, &mut f); + for_operand(o1, &mut f, &mut self.projection_store); + for_operand(o2, &mut f, &mut self.projection_store); } Rvalue::Aggregate(_, ops) => { for op in ops.iter_mut() { - for_operand(op, &mut f); + for_operand(op, &mut f, &mut self.projection_store); } } } } - StatementKind::Deinit(p) => f(p), + StatementKind::Deinit(p) => f(p, &mut self.projection_store), StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), @@ -1053,7 +1117,9 @@ impl MirBody { } match &mut block.terminator { Some(x) => match &mut x.kind { - TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f), + TerminatorKind::SwitchInt { discr, .. } => { + for_operand(discr, &mut f, &mut self.projection_store) + } TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } @@ -1063,23 +1129,24 @@ impl MirBody { | TerminatorKind::Return | TerminatorKind::Unreachable => (), TerminatorKind::Drop { place, .. } => { - f(place); + f(place, &mut self.projection_store); } TerminatorKind::DropAndReplace { place, value, .. } => { - f(place); - for_operand(value, &mut f); + f(place, &mut self.projection_store); + for_operand(value, &mut f, &mut self.projection_store); } TerminatorKind::Call { func, args, destination, .. } => { - for_operand(func, &mut f); - args.iter_mut().for_each(|x| for_operand(x, &mut f)); - f(destination); + for_operand(func, &mut f, &mut self.projection_store); + args.iter_mut() + .for_each(|x| for_operand(x, &mut f, &mut self.projection_store)); + f(destination, &mut self.projection_store); } TerminatorKind::Assert { cond, .. } => { - for_operand(cond, &mut f); + for_operand(cond, &mut f, &mut self.projection_store); } TerminatorKind::Yield { value, resume_arg, .. } => { - for_operand(value, &mut f); - f(resume_arg); + for_operand(value, &mut f, &mut self.projection_store); + f(resume_arg, &mut self.projection_store); } }, None => (), @@ -1096,7 +1163,9 @@ impl MirBody { binding_locals, param_locals, closures, + projection_store, } = self; + projection_store.shrink_to_fit(); basic_blocks.shrink_to_fit(); locals.shrink_to_fit(); binding_locals.shrink_to_fit(); diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index 20a439bd643..7f80c7be80b 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -88,7 +88,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec Operand::Copy(p) | Operand::Move(p) => { let mut ty: Ty = body.locals[p.local].ty.clone(); let mut is_dereference_of_ref = false; - for proj in &*p.projection { + for proj in p.projection.lookup(&body.projection_store) { if *proj == ProjectionElem::Deref && ty.as_reference().is_some() { is_dereference_of_ref = true; } @@ -195,7 +195,7 @@ enum ProjectionCase { fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase { let mut is_part_of = false; let mut ty = body.locals[lvalue.local].ty.clone(); - for proj in lvalue.projection.iter() { + for proj in lvalue.projection.lookup(&body.projection_store).iter() { match proj { ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw ProjectionElem::Deref // It's direct in case of `Box` @@ -254,7 +254,7 @@ fn ever_initialized_map( for statement in &block.statements { match &statement.kind { StatementKind::Assign(p, _) => { - if p.projection.len() == 0 && p.local == l { + if p.projection.lookup(&body.projection_store).len() == 0 && p.local == l { is_ever_initialized = true; } } @@ -289,7 +289,9 @@ fn ever_initialized_map( | TerminatorKind::Return | TerminatorKind::Unreachable => (), TerminatorKind::Call { target, cleanup, destination, .. } => { - if destination.projection.len() == 0 && destination.local == l { + if destination.projection.lookup(&body.projection_store).len() == 0 + && destination.local == l + { is_ever_initialized = true; } target @@ -389,7 +391,7 @@ fn mutability_of_locals( | TerminatorKind::Assert { .. } | TerminatorKind::Yield { .. } => (), TerminatorKind::Call { destination, .. } => { - if destination.projection.len() == 0 { + if destination.projection.lookup(&body.projection_store).len() == 0 { if ever_init_map.get(destination.local).copied().unwrap_or_default() { push_mut_span(destination.local, MirSpan::Unknown); } else { diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 3944feb128c..28c7a746525 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -46,8 +46,8 @@ use crate::{ use super::{ return_slot, AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError, - MirSpan, Operand, Place, PlaceElem, ProjectionElem, Rvalue, StatementKind, TerminatorKind, - UnOp, + MirSpan, Operand, Place, PlaceElem, ProjectionElem, ProjectionStore, Rvalue, StatementKind, + TerminatorKind, UnOp, }; mod shim; @@ -485,17 +485,18 @@ struct DropFlags { } impl DropFlags { - fn add_place(&mut self, p: Place) { - if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) { + fn add_place(&mut self, p: Place, store: &ProjectionStore) { + if p.iterate_over_parents(store).any(|it| self.need_drop.contains(&it)) { return; } - self.need_drop.retain(|it| !p.is_parent(it)); + self.need_drop.retain(|it| !p.is_parent(it, store)); self.need_drop.insert(p); } - fn remove_place(&mut self, p: &Place) -> bool { + fn remove_place(&mut self, p: &Place, store: &ProjectionStore) -> bool { // FIXME: replace parents with parts - if let Some(parent) = p.iterate_over_parents().find(|it| self.need_drop.contains(&it)) { + if let Some(parent) = p.iterate_over_parents(store).find(|it| self.need_drop.contains(&it)) + { self.need_drop.remove(&parent); return true; } @@ -656,7 +657,7 @@ impl Evaluator<'_> { let mut addr = locals.ptr[p.local].addr; let mut ty: Ty = locals.body.locals[p.local].ty.clone(); let mut metadata: Option = None; // locals are always sized - for proj in &*p.projection { + for proj in p.projection.lookup(&locals.body.projection_store) { let prev_ty = ty.clone(); ty = self.projected_ty(ty, proj.clone()); match proj { @@ -837,7 +838,9 @@ impl Evaluator<'_> { let addr = self.place_addr(l, &locals)?; let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?; self.write_memory(addr, &result)?; - locals.drop_flags.add_place(l.clone()); + locals + .drop_flags + .add_place(l.clone(), &locals.body.projection_store); } StatementKind::Deinit(_) => not_supported!("de-init statement"), StatementKind::StorageLive(_) @@ -889,7 +892,9 @@ impl Evaluator<'_> { )?, it => not_supported!("unknown function type {it:?}"), }; - locals.drop_flags.add_place(destination.clone()); + locals + .drop_flags + .add_place(destination.clone(), &locals.body.projection_store); if let Some(stack_frame) = stack_frame { self.code_stack.push(my_stack_frame); current_block_idx = stack_frame.locals.body.start_block; @@ -970,7 +975,7 @@ impl Evaluator<'_> { ) -> Result<()> { let mut remain_args = body.param_locals.len(); for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) { - locals.drop_flags.add_place(l.into()); + locals.drop_flags.add_place(l.into(), &locals.body.projection_store); match value { IntervalOrOwned::Owned(value) => interval.write_from_bytes(self, &value)?, IntervalOrOwned::Borrowed(value) => interval.write_from_interval(self, value)?, @@ -1646,7 +1651,7 @@ impl Evaluator<'_> { fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result { Ok(match it { Operand::Copy(p) | Operand::Move(p) => { - locals.drop_flags.remove_place(p); + locals.drop_flags.remove_place(p, &locals.body.projection_store); self.eval_place(p, locals)? } Operand::Static(st) => { @@ -2468,7 +2473,7 @@ impl Evaluator<'_> { fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<()> { let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; - if !locals.drop_flags.remove_place(place) { + if !locals.drop_flags.remove_place(place, &locals.body.projection_store) { return Ok(()); } let metadata = match metadata { diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index b6408cea502..bceeb185003 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -244,6 +244,7 @@ impl<'ctx> MirLowerCtx<'ctx> { let locals = Arena::new(); let binding_locals: ArenaMap = ArenaMap::new(); let mir = MirBody { + projection_store: ProjectionStore::default(), basic_blocks, locals, start_block, @@ -809,36 +810,34 @@ impl<'ctx> MirLowerCtx<'ctx> { current = c; operands[u32::from(field_id.into_raw()) as usize] = Some(op); } - self.push_assignment( - current, - place, - Rvalue::Aggregate( - AggregateKind::Adt(variant_id, subst), - match spread_place { - Some(sp) => operands - .into_iter() - .enumerate() - .map(|(i, it)| match it { - Some(it) => it, - None => { - let p = - sp.project(ProjectionElem::Field(FieldId { - parent: variant_id, - local_id: LocalFieldId::from_raw( - RawIdx::from(i as u32), - ), - })); - Operand::Copy(p) - } - }) - .collect(), - None => operands.into_iter().collect::>().ok_or( - MirLowerError::TypeError("missing field in record literal"), - )?, - }, - ), - expr_id.into(), + let rvalue = Rvalue::Aggregate( + AggregateKind::Adt(variant_id, subst), + match spread_place { + Some(sp) => operands + .into_iter() + .enumerate() + .map(|(i, it)| match it { + Some(it) => it, + None => { + let p = sp.project( + ProjectionElem::Field(FieldId { + parent: variant_id, + local_id: LocalFieldId::from_raw(RawIdx::from( + i as u32, + )), + }), + &mut self.result.projection_store, + ); + Operand::Copy(p) + } + }) + .collect(), + None => operands.into_iter().collect::>().ok_or( + MirLowerError::TypeError("missing field in record literal"), + )?, + }, ); + self.push_assignment(current, place, rvalue, expr_id.into()); Ok(Some(current)) } VariantId::UnionId(union_id) => { @@ -847,10 +846,10 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let local_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; - let place = place.project(PlaceElem::Field(FieldId { - parent: union_id.into(), - local_id, - })); + let place = place.project( + PlaceElem::Field(FieldId { parent: union_id.into(), local_id }), + &mut self.result.projection_store, + ); self.lower_expr_to_place(*expr, place, current) } } @@ -904,7 +903,7 @@ impl<'ctx> MirLowerCtx<'ctx> { else { return Ok(None); }; - let p = place.project(ProjectionElem::Deref); + let p = place.project(ProjectionElem::Deref, &mut self.result.projection_store); self.push_assignment(current, p, operand.into(), expr_id.into()); Ok(Some(current)) } @@ -1126,27 +1125,31 @@ impl<'ctx> MirLowerCtx<'ctx> { for capture in captures.iter() { let p = Place { local: self.binding_local(capture.place.local)?, - projection: capture - .place - .projections - .clone() - .into_iter() - .map(|it| match it { - ProjectionElem::Deref => ProjectionElem::Deref, - ProjectionElem::Field(it) => ProjectionElem::Field(it), - ProjectionElem::TupleOrClosureField(it) => { - ProjectionElem::TupleOrClosureField(it) - } - ProjectionElem::ConstantIndex { offset, from_end } => { - ProjectionElem::ConstantIndex { offset, from_end } - } - ProjectionElem::Subslice { from, to } => { - ProjectionElem::Subslice { from, to } - } - ProjectionElem::OpaqueCast(it) => ProjectionElem::OpaqueCast(it), - ProjectionElem::Index(it) => match it {}, - }) - .collect(), + projection: self.result.projection_store.intern( + capture + .place + .projections + .clone() + .into_iter() + .map(|it| match it { + ProjectionElem::Deref => ProjectionElem::Deref, + ProjectionElem::Field(it) => ProjectionElem::Field(it), + ProjectionElem::TupleOrClosureField(it) => { + ProjectionElem::TupleOrClosureField(it) + } + ProjectionElem::ConstantIndex { offset, from_end } => { + ProjectionElem::ConstantIndex { offset, from_end } + } + ProjectionElem::Subslice { from, to } => { + ProjectionElem::Subslice { from, to } + } + ProjectionElem::OpaqueCast(it) => { + ProjectionElem::OpaqueCast(it) + } + ProjectionElem::Index(it) => match it {}, + }) + .collect(), + ), }; match &capture.kind { CaptureKind::ByRef(bk) => { @@ -1261,12 +1264,11 @@ impl<'ctx> MirLowerCtx<'ctx> { match &self.body.exprs[lhs] { Expr::Tuple { exprs, is_assignee_expr: _ } => { for (i, expr) in exprs.iter().enumerate() { - let Some(c) = self.lower_destructing_assignment( - current, - *expr, - rhs.project(ProjectionElem::TupleOrClosureField(i)), - span, - )? + let rhs = rhs.project( + ProjectionElem::TupleOrClosureField(i), + &mut self.result.projection_store, + ); + let Some(c) = self.lower_destructing_assignment(current, *expr, rhs, span)? else { return Ok(None); }; @@ -1323,17 +1325,21 @@ impl<'ctx> MirLowerCtx<'ctx> { placeholder_subst } - fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> { + fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Result<()> { if let Expr::Field { expr, name } = &self.body[expr_id] { if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) { let index = name .as_tuple_index() .ok_or(MirLowerError::TypeError("named field on tuple"))?; - *place = place.project(ProjectionElem::TupleOrClosureField(index)) + *place = place.project( + ProjectionElem::TupleOrClosureField(index), + &mut self.result.projection_store, + ) } else { let field = self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?; - *place = place.project(ProjectionElem::Field(field)); + *place = + place.project(ProjectionElem::Field(field), &mut self.result.projection_store); } } else { not_supported!("") @@ -1995,13 +2001,14 @@ pub fn mir_body_for_closure_query( FnTrait::FnOnce => vec![], FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref], }; - ctx.result.walk_places(|p| { + ctx.result.walk_places(|p, store| { if let Some(it) = upvar_map.get(&p.local) { let r = it.iter().find(|it| { - if p.projection.len() < it.0.place.projections.len() { + if p.projection.lookup(&store).len() < it.0.place.projections.len() { return false; } - for (it, y) in p.projection.iter().zip(it.0.place.projections.iter()) { + for (it, y) in p.projection.lookup(&store).iter().zip(it.0.place.projections.iter()) + { match (it, y) { (ProjectionElem::Deref, ProjectionElem::Deref) => (), (ProjectionElem::Field(it), ProjectionElem::Field(y)) if it == y => (), @@ -2019,13 +2026,18 @@ pub fn mir_body_for_closure_query( p.local = closure_local; let mut next_projs = closure_projection.clone(); next_projs.push(PlaceElem::TupleOrClosureField(it.1)); - let prev_projs = mem::take(&mut p.projection); + let prev_projs = p.projection; if it.0.kind != CaptureKind::ByValue { next_projs.push(ProjectionElem::Deref); } - next_projs - .extend(prev_projs.iter().cloned().skip(it.0.place.projections.len())); - p.projection = next_projs.into(); + next_projs.extend( + prev_projs + .lookup(&store) + .iter() + .cloned() + .skip(it.0.place.projections.len()), + ); + p.projection = store.intern(next_projs.into()); } None => err = Some(p.clone()), } diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs index 213f151ab67..8c078eb4ad7 100644 --- a/crates/hir-ty/src/mir/lower/as_place.rs +++ b/crates/hir-ty/src/mir/lower/as_place.rs @@ -70,7 +70,7 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - it.0 = it.0.project(ProjectionElem::Deref); + it.0 = it.0.project(ProjectionElem::Deref, &mut self.result.projection_store); Ok(Some(it)) } Adjust::Deref(Some(od)) => { @@ -152,7 +152,10 @@ impl MirLowerCtx<'_> { Operand::Static(s).into(), expr_id.into(), ); - Ok(Some((temp.project(ProjectionElem::Deref), current))) + Ok(Some(( + temp.project(ProjectionElem::Deref, &mut self.result.projection_store), + current, + ))) } _ => try_rvalue(self), } @@ -203,7 +206,7 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - r = r.project(ProjectionElem::Deref); + r = r.project(ProjectionElem::Deref, &mut self.result.projection_store); Ok(Some((r, current))) } _ => try_rvalue(self), @@ -267,7 +270,8 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - p_base = p_base.project(ProjectionElem::Index(l_index)); + p_base = p_base + .project(ProjectionElem::Index(l_index), &mut self.result.projection_store); Ok(Some((p_base, current))) } _ => try_rvalue(self), @@ -308,7 +312,7 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - result = result.project(ProjectionElem::Deref); + result = result.project(ProjectionElem::Deref, &mut self.result.projection_store); Ok(Some((result, current))) } @@ -363,7 +367,7 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - result = result.project(ProjectionElem::Deref); + result = result.project(ProjectionElem::Deref, &mut self.result.projection_store); Ok(Some((result, current))) } } diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 1cdfd919742..270f75ad967 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -81,13 +81,16 @@ impl MirLowerCtx<'_> { mode: MatchingMode, ) -> Result<(BasicBlockId, Option)> { let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default(); - cond_place.projection = cond_place - .projection - .iter() - .cloned() - .chain((0..cnt).map(|_| ProjectionElem::Deref)) - .collect::>() - .into(); + cond_place.projection = self.result.projection_store.intern( + cond_place + .projection + .lookup(&self.result.projection_store) + .iter() + .cloned() + .chain((0..cnt).map(|_| ProjectionElem::Deref)) + .collect::>() + .into(), + ); Ok(match &self.body.pats[pattern] { Pat::Missing => return Err(MirLowerError::IncompletePattern), Pat::Wild => (current, current_else), @@ -262,20 +265,23 @@ impl MirLowerCtx<'_> { } } for (i, &pat) in prefix.iter().enumerate() { - let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex { - offset: i as u64, - from_end: false, - }); + let next_place = (&mut cond_place).project( + ProjectionElem::ConstantIndex { offset: i as u64, from_end: false }, + &mut self.result.projection_store, + ); (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } if let Some(slice) = slice { if mode == MatchingMode::Bind { if let Pat::Bind { id, subpat: _ } = self.body[*slice] { - let next_place = (&mut cond_place).project(ProjectionElem::Subslice { - from: prefix.len() as u64, - to: suffix.len() as u64, - }); + let next_place = (&mut cond_place).project( + ProjectionElem::Subslice { + from: prefix.len() as u64, + to: suffix.len() as u64, + }, + &mut self.result.projection_store, + ); (current, current_else) = self.pattern_match_binding( id, next_place, @@ -287,10 +293,10 @@ impl MirLowerCtx<'_> { } } for (i, &pat) in suffix.iter().enumerate() { - let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex { - offset: i as u64, - from_end: true, - }); + let next_place = (&mut cond_place).project( + ProjectionElem::ConstantIndex { offset: i as u64, from_end: true }, + &mut self.result.projection_store, + ); (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } @@ -412,13 +418,11 @@ impl MirLowerCtx<'_> { mode, )? } - Pat::Ref { pat, mutability: _ } => self.pattern_match_inner( - current, - current_else, - cond_place.project(ProjectionElem::Deref), - *pat, - mode, - )?, + Pat::Ref { pat, mutability: _ } => { + let cond_place = + cond_place.project(ProjectionElem::Deref, &mut self.result.projection_store); + self.pattern_match_inner(current, current_else, cond_place, *pat, mode)? + } Pat::Box { .. } => not_supported!("box pattern"), Pat::ConstBlock(_) => not_supported!("const block pattern"), }) @@ -594,7 +598,7 @@ impl MirLowerCtx<'_> { mode: MatchingMode, ) -> Result<(BasicBlockId, Option)> { for (proj, arg) in args { - let cond_place = cond_place.project(proj); + let cond_place = cond_place.project(proj, &mut self.result.projection_store); (current, current_else) = self.pattern_match_inner(current, current_else, cond_place, arg, mode)?; } diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index 781ffaecad5..0108859ff32 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -329,7 +329,7 @@ impl<'a> MirPrettyCtx<'a> { } } } - f(self, p.local, &p.projection); + f(self, p.local, &p.projection.lookup(&self.body.projection_store)); } fn operand(&mut self, r: &Operand) { From 7e786ea4cf8dc82f04d6d90f62b6bd83e71bfdc4 Mon Sep 17 00:00:00 2001 From: Andy Caldwell Date: Wed, 9 Aug 2023 15:57:16 +0100 Subject: [PATCH 114/250] Rework no_coverage to coverage(off) --- crates/hir-def/src/attr/builtin.rs | 2 +- crates/ide-db/src/generated/lints.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/hir-def/src/attr/builtin.rs b/crates/hir-def/src/attr/builtin.rs index cead64a3374..9bd0c30b105 100644 --- a/crates/hir-def/src/attr/builtin.rs +++ b/crates/hir-def/src/attr/builtin.rs @@ -239,7 +239,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(List: "address, kcfi, memory, thread"), DuplicatesOk, experimental!(no_sanitize) ), - gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)), + gated!(coverage, Normal, template!(Word, List: "on|off"), WarnFollowing, experimental!(coverage)), ungated!( doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk diff --git a/crates/ide-db/src/generated/lints.rs b/crates/ide-db/src/generated/lints.rs index 49b37024a5e..57563a17483 100644 --- a/crates/ide-db/src/generated/lints.rs +++ b/crates/ide-db/src/generated/lints.rs @@ -3505,8 +3505,8 @@ This serves two purposes: "##, }, Lint { - label: "no_coverage", - description: r##"# `no_coverage` + label: "coverage", + description: r##"# `coverage` The tracking issue for this feature is: [#84605] @@ -3514,7 +3514,7 @@ The tracking issue for this feature is: [#84605] --- -The `no_coverage` attribute can be used to selectively disable coverage +The `coverage` attribute can be used to selectively disable coverage instrumentation in an annotated function. This might be useful to: - Avoid instrumentation overhead in a performance critical function @@ -3524,14 +3524,14 @@ instrumentation in an annotated function. This might be useful to: ## Example ```rust -#![feature(no_coverage)] +#![feature(coverage)] // `foo()` will get coverage instrumentation (by default) fn foo() { // ... } -#[no_coverage] +#[coverage(off)] fn bar() { // ... } From 297ed70a238aca12db010046934cbd3d60b6dc84 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 8 Sep 2023 13:54:07 +0200 Subject: [PATCH 115/250] Clear native diagnostics on file closing --- crates/rust-analyzer/src/handlers/notification.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index 93f33d7cac9..f9070d27353 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -113,6 +113,10 @@ pub(crate) fn handle_did_close_text_document( tracing::error!("orphan DidCloseTextDocument: {}", path); } + if let Some(file_id) = state.vfs.read().0.file_id(&path) { + state.diagnostics.clear_native_for(file_id); + } + state.semantic_tokens_cache.lock().remove(¶ms.text_document.uri); if let Some(path) = path.as_path() { From 8654a098c76ba0cbfa31d7ea668c3de5a48571a1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 8 Sep 2023 14:31:26 +0200 Subject: [PATCH 116/250] Diagnose mismatched arg count for tuple struct patterns --- crates/hir-ty/src/infer.rs | 5 + crates/hir-ty/src/infer/pat.rs | 15 ++- crates/hir/src/diagnostics.rs | 8 ++ crates/hir/src/lib.rs | 24 +++- .../src/handlers/mismatched_arg_count.rs | 108 +++++++++++++++--- .../src/handlers/missing_match_arms.rs | 3 + crates/ide-diagnostics/src/lib.rs | 1 + 7 files changed, 144 insertions(+), 20 deletions(-) diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 0fb4934444b..794d99964e4 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -228,6 +228,11 @@ pub enum InferenceDiagnostic { expected: usize, found: usize, }, + MismatchedTupleStructPatArgCount { + pat: ExprOrPatId, + expected: usize, + found: usize, + }, ExpectedFunction { call_expr: ExprId, found: Ty, diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 5da0ab76b88..7d88e42e52a 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -15,7 +15,8 @@ use crate::{ infer::{BindingMode, Expectation, InferenceContext, TypeMismatch}, lower::lower_to_chalk_mutability, primitive::UintTy, - static_lifetime, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt, TyKind, + static_lifetime, InferenceDiagnostic, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt, + TyKind, }; /// Used to generalize patterns and assignee expressions. @@ -74,6 +75,18 @@ impl InferenceContext<'_> { if let Some(variant) = def { self.write_variant_resolution(id.into(), variant); } + if let Some(var) = &var_data { + let cmp = if ellipsis.is_some() { usize::gt } else { usize::ne }; + + if cmp(&subs.len(), &var.fields().len()) { + self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount { + pat: id.into(), + expected: var.fields().len(), + found: subs.len(), + }); + } + } + self.unify(&ty, expected); let substs = diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 80c3bcdca8b..b8b997cc506 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -43,6 +43,7 @@ diagnostics![ MacroExpansionParseError, MalformedDerive, MismatchedArgCount, + MismatchedTupleStructPatArgCount, MissingFields, MissingMatchArms, MissingUnsafe, @@ -182,6 +183,13 @@ pub struct PrivateAssocItem { pub item: AssocItem, } +#[derive(Debug)] +pub struct MismatchedTupleStructPatArgCount { + pub expr_or_pat: InFile, AstPtr>>, + pub expected: usize, + pub found: usize, +} + #[derive(Debug)] pub struct ExpectedFunction { pub call: InFile>, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 91443238c62..47a2a25b6b7 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -92,9 +92,10 @@ pub use crate::{ diagnostics::{ AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode, IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, - MacroExpansionParseError, MalformedDerive, MismatchedArgCount, MissingFields, - MissingMatchArms, MissingUnsafe, MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, - PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel, + MacroExpansionParseError, MalformedDerive, MismatchedArgCount, + MismatchedTupleStructPatArgCount, MissingFields, MissingMatchArms, MissingUnsafe, + MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, + ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel, UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut, @@ -1597,6 +1598,23 @@ impl DefWithBody { .into(), ) } + &hir_ty::InferenceDiagnostic::MismatchedTupleStructPatArgCount { + pat, + expected, + found, + } => { + let expr_or_pat = match pat { + ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left), + ExprOrPatId::PatId(pat) => source_map + .pat_syntax(pat) + .expect("unexpected synthetic") + .map(|it| it.unwrap_left()) + .map(Either::Right), + }; + acc.push( + MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into(), + ) + } } } for (pat_or_expr, mismatch) in infer.type_mismatches() { diff --git a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 6238c7e09e9..8265e0b1c11 100644 --- a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -1,10 +1,37 @@ +use either::Either; +use hir::InFile; use syntax::{ ast::{self, HasArgList}, - AstNode, TextRange, + AstNode, SyntaxNodePtr, TextRange, }; use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext}; +// Diagnostic: mismatched-tuple-struct-pat-arg-count +// +// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. +pub(crate) fn mismatched_tuple_struct_pat_arg_count( + ctx: &DiagnosticsContext<'_>, + d: &hir::MismatchedTupleStructPatArgCount, +) -> Diagnostic { + let s = if d.found == 1 { "" } else { "s" }; + let s2 = if d.expected == 1 { "" } else { "s" }; + let message = format!( + "this pattern has {} field{s}, but the corresponding tuple struct has {} field{s2}", + d.found, d.expected + ); + Diagnostic::new( + DiagnosticCode::RustcHardError("E0023"), + message, + invalid_args_range( + ctx, + d.expr_or_pat.clone().map(|it| it.either(Into::into, Into::into)), + d.expected, + d.found, + ), + ) +} + // Diagnostic: mismatched-arg-count // // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. @@ -14,31 +41,63 @@ pub(crate) fn mismatched_arg_count( ) -> Diagnostic { let s = if d.expected == 1 { "" } else { "s" }; let message = format!("expected {} argument{s}, found {}", d.expected, d.found); - Diagnostic::new(DiagnosticCode::RustcHardError("E0107"), message, invalid_args_range(ctx, d)) + Diagnostic::new( + DiagnosticCode::RustcHardError("E0107"), + message, + invalid_args_range(ctx, d.call_expr.clone().map(Into::into), d.expected, d.found), + ) } -fn invalid_args_range(ctx: &DiagnosticsContext<'_>, d: &hir::MismatchedArgCount) -> TextRange { - adjusted_display_range::(ctx, d.call_expr.clone().map(|it| it.into()), &|expr| { - let arg_list = match expr { - ast::Expr::CallExpr(call) => call.arg_list()?, - ast::Expr::MethodCallExpr(call) => call.arg_list()?, +fn invalid_args_range( + ctx: &DiagnosticsContext<'_>, + source: InFile, + expected: usize, + found: usize, +) -> TextRange { + adjusted_display_range::>(ctx, source, &|expr| { + let (text_range, r_paren_token, expected_arg) = match expr { + Either::Left(ast::Expr::CallExpr(call)) => { + let arg_list = call.arg_list()?; + ( + arg_list.syntax().text_range(), + arg_list.r_paren_token(), + arg_list.args().nth(expected).map(|it| it.syntax().text_range()), + ) + } + Either::Left(ast::Expr::MethodCallExpr(call)) => { + let arg_list = call.arg_list()?; + ( + arg_list.syntax().text_range(), + arg_list.r_paren_token(), + arg_list.args().nth(expected).map(|it| it.syntax().text_range()), + ) + } + Either::Right(pat) => { + let r_paren = pat.r_paren_token()?; + let l_paren = pat.l_paren_token()?; + ( + l_paren.text_range().cover(r_paren.text_range()), + Some(r_paren), + pat.fields().nth(expected).map(|it| it.syntax().text_range()), + ) + } _ => return None, }; - if d.found < d.expected { - if d.found == 0 { - return Some(arg_list.syntax().text_range()); + if found < expected { + if found == 0 { + return Some(text_range); } - if let Some(r_paren) = arg_list.r_paren_token() { + if let Some(r_paren) = r_paren_token { return Some(r_paren.text_range()); } } - if d.expected < d.found { - if d.expected == 0 { - return Some(arg_list.syntax().text_range()); + if expected < found { + if expected == 0 { + return Some(text_range); } - let zip = arg_list.args().nth(d.expected).zip(arg_list.r_paren_token()); + let zip = expected_arg.zip(r_paren_token); if let Some((arg, r_paren)) = zip { - return Some(arg.syntax().text_range().cover(r_paren.text_range())); + return Some(arg.cover(r_paren.text_range())); } } @@ -331,4 +390,21 @@ fn g() { "#, ) } + + #[test] + fn tuple_struct_pat() { + check_diagnostics( + r#" +struct S(u32, u32); +fn f( + S(a, b, c): S, + // ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields + S(): S, + // ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 2 fields + S(e, f, .., g, d): S + // ^^^^^^^^^ error: this pattern has 4 fields, but the corresponding tuple struct has 2 fields +) {} +"#, + ) + } } diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 82a9a3bd559..d5115c2192b 100644 --- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -319,6 +319,7 @@ fn main() { match Either::A { Either::A => (), Either::B() => (), + // ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 1 field } } "#, @@ -334,9 +335,11 @@ enum A { B(isize, isize), C } fn main() { match A::B(1, 2) { A::B(_, _, _) => (), + // ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields } match A::B(1, 2) { A::C(_) => (), + // ^^^ error: this pattern has 1 field, but the corresponding tuple struct has 0 fields } } "#, diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index b1b9b4b8ea5..16ddb2aaf38 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -369,6 +369,7 @@ pub fn diagnostics( AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled), AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d), AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d), + AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d), }; res.push(d) } From 0bf6ffaf822d6057d114425dfb59de99619247e4 Mon Sep 17 00:00:00 2001 From: David Barsky Date: Fri, 8 Sep 2023 14:07:59 -0400 Subject: [PATCH 117/250] Update crates/project-model/src/rustc_cfg.rs Co-authored-by: Lukas Wirth --- crates/project-model/src/rustc_cfg.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/project-model/src/rustc_cfg.rs b/crates/project-model/src/rustc_cfg.rs index 905adff4e5c..c5d55f7d217 100644 --- a/crates/project-model/src/rustc_cfg.rs +++ b/crates/project-model/src/rustc_cfg.rs @@ -102,6 +102,5 @@ fn get_rust_cfgs( cmd.args(["--target", target]); } - let out = utf8_stdout(cmd).context("Unable to run `rustc`")?; - Ok(out) + utf8_stdout(cmd).context("Unable to run `rustc`") } From 55c75450fb3143f5624c4c59d9c420cde6a54448 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 8 Sep 2023 23:19:30 +0200 Subject: [PATCH 118/250] Diagnose private fields in record constructor --- crates/hir-def/src/data/adt.rs | 1 + crates/hir-ty/src/infer.rs | 1 + crates/hir-ty/src/infer/expr.rs | 73 +++++++++++++------ crates/hir/src/diagnostics.rs | 1 + crates/hir/src/lib.rs | 4 +- .../src/handlers/no_such_field.rs | 31 +++++++- 6 files changed, 84 insertions(+), 27 deletions(-) diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index c8df3f3f96a..224f7328f8c 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -447,6 +447,7 @@ impl VariantData { } } + // FIXME: Linear lookup pub fn field(&self, name: &Name) -> Option { self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None }) } diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 794d99964e4..3423e0f0120 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -195,6 +195,7 @@ pub(crate) type InferResult = Result, TypeError>; pub enum InferenceDiagnostic { NoSuchField { expr: ExprId, + private: bool, }, PrivateField { expr: ExprId, diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 555a9fae48e..e283db81fe8 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -514,9 +514,6 @@ impl InferenceContext<'_> { } Expr::RecordLit { path, fields, spread, .. } => { let (ty, def_id) = self.resolve_variant(path.as_deref(), false); - if let Some(variant) = def_id { - self.write_variant_resolution(tgt_expr.into(), variant); - } if let Some(t) = expected.only_has_type(&mut self.table) { self.unify(&ty, &t); @@ -526,26 +523,56 @@ impl InferenceContext<'_> { .as_adt() .map(|(_, s)| s.clone()) .unwrap_or_else(|| Substitution::empty(Interner)); - let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default(); - let variant_data = def_id.map(|it| it.variant_data(self.db.upcast())); - for field in fields.iter() { - let field_def = - variant_data.as_ref().and_then(|it| match it.field(&field.name) { - Some(local_id) => Some(FieldId { parent: def_id.unwrap(), local_id }), - None => { - self.push_diagnostic(InferenceDiagnostic::NoSuchField { - expr: field.expr, - }); - None - } - }); - let field_ty = field_def.map_or(self.err_ty(), |it| { - field_types[it.local_id].clone().substitute(Interner, &substs) - }); - // Field type might have some unknown types - // FIXME: we may want to emit a single type variable for all instance of type fields? - let field_ty = self.insert_type_vars(field_ty); - self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty)); + if let Some(variant) = def_id { + self.write_variant_resolution(tgt_expr.into(), variant); + } + match def_id { + Some(_) if fields.is_empty() => {} + Some(def) => { + let field_types = self.db.field_types(def); + let variant_data = def.variant_data(self.db.upcast()); + let visibilities = self.db.field_visibilities(def); + for field in fields.iter() { + let field_def = { + match variant_data.field(&field.name) { + Some(local_id) => { + if !visibilities[local_id].is_visible_from( + self.db.upcast(), + self.resolver.module(), + ) { + self.push_diagnostic( + InferenceDiagnostic::NoSuchField { + expr: field.expr, + private: true, + }, + ); + } + Some(FieldId { parent: def, local_id }) + } + None => { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + expr: field.expr, + private: false, + }); + None + } + } + }; + let field_ty = field_def.map_or(self.err_ty(), |it| { + field_types[it.local_id].clone().substitute(Interner, &substs) + }); + + // Field type might have some unknown types + // FIXME: we may want to emit a single type variable for all instance of type fields? + let field_ty = self.insert_type_vars(field_ty); + self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty)); + } + } + None => { + for field in fields.iter() { + self.infer_expr_coerce(field.expr, &Expectation::None); + } + } } if let Some(expr) = spread { self.infer_expr(*expr, &Expectation::has_type(ty.clone())); diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index b8b997cc506..d9a6e6fcd55 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -174,6 +174,7 @@ pub struct MalformedDerive { #[derive(Debug)] pub struct NoSuchField { pub field: InFile>, + pub private: bool, } #[derive(Debug)] diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 47a2a25b6b7..ebb8539a66d 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1505,9 +1505,9 @@ impl DefWithBody { let expr_syntax = |expr| source_map.expr_syntax(expr).expect("unexpected synthetic"); for d in &infer.diagnostics { match d { - &hir_ty::InferenceDiagnostic::NoSuchField { expr } => { + &hir_ty::InferenceDiagnostic::NoSuchField { expr, private } => { let field = source_map.field_syntax(expr); - acc.push(NoSuchField { field }.into()) + acc.push(NoSuchField { field, private }.into()) } &hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { acc.push( diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs index a34a5824f29..34f2b7bbf15 100644 --- a/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -14,14 +14,22 @@ use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext}; pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( ctx, - DiagnosticCode::RustcHardError("E0559"), - "no such field", + if d.private { + DiagnosticCode::RustcHardError("E0451") + } else { + DiagnosticCode::RustcHardError("E0559") + }, + if d.private { "field is private" } else { "no such field" }, d.field.clone().map(|it| it.into()), ) .with_fixes(fixes(ctx, d)) } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option> { + if d.private { + // FIXME: quickfix to add required visibility + return None; + } let root = ctx.sema.db.parse_or_expand(d.field.file_id); missing_record_expr_field_fixes( &ctx.sema, @@ -292,6 +300,25 @@ fn main() { 0$0: 0 } } +"#, + ) + } + + #[test] + fn test_struct_field_private() { + check_diagnostics( + r#" +mod m { + pub struct Struct { + field: u32 + } +} +fn main() { + m::Struct { + field: 0 + //^^^^^^^^ error: field is private + }; +} "#, ) } From 8f5fee4a5aab72399da267fca256090c03c55ff6 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 9 Sep 2023 10:45:29 +0200 Subject: [PATCH 119/250] Diagnose incorrect and private fields in record structs --- crates/hir-def/src/body.rs | 9 +- crates/hir-def/src/body/lower.rs | 15 +-- crates/hir-ty/src/infer.rs | 2 +- crates/hir-ty/src/infer/expr.rs | 12 +- crates/hir-ty/src/infer/pat.rs | 115 ++++++++++++++---- crates/hir-ty/src/mir.rs | 4 +- crates/hir/src/diagnostics.rs | 2 +- crates/hir/src/lib.rs | 19 +-- .../src/handlers/missing_match_arms.rs | 1 + .../src/handlers/no_such_field.rs | 94 ++++++++++---- 10 files changed, 192 insertions(+), 81 deletions(-) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index f8d492d0e52..26fd58c0a40 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -65,6 +65,8 @@ pub type LabelSource = InFile; pub type FieldPtr = AstPtr; pub type FieldSource = InFile; +pub type PatFieldPtr = AstPtr; +pub type PatFieldSource = InFile; /// An item body together with the mapping from syntax nodes to HIR expression /// IDs. This is needed to go from e.g. a position in a file to the HIR @@ -90,8 +92,8 @@ pub struct BodySourceMap { /// We don't create explicit nodes for record fields (`S { record_field: 92 }`). /// Instead, we use id of expression (`92`) to identify the field. - field_map: FxHashMap, field_map_back: FxHashMap, + pat_field_map_back: FxHashMap, expansions: FxHashMap>, HirFileId>, @@ -375,9 +377,8 @@ impl BodySourceMap { self.field_map_back[&expr].clone() } - pub fn node_field(&self, node: InFile<&ast::RecordExprField>) -> Option { - let src = node.map(AstPtr::new); - self.field_map.get(&src).cloned() + pub fn pat_field_syntax(&self, pat: PatId) -> PatFieldSource { + self.pat_field_map_back[&pat].clone() } pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option { diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 38073ce3da5..cc02df80a8f 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -446,7 +446,6 @@ impl ExprCollector<'_> { None => self.missing_expr(), }; let src = self.expander.to_source(AstPtr::new(&field)); - self.source_map.field_map.insert(src.clone(), expr); self.source_map.field_map_back.insert(expr, src); Some(RecordLitField { name, expr }) }) @@ -1330,23 +1329,21 @@ impl ExprCollector<'_> { ast::Pat::RecordPat(p) => { let path = p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); - let args = p - .record_pat_field_list() - .expect("every struct should have a field list") + let record_pat_field_list = + &p.record_pat_field_list().expect("every struct should have a field list"); + let args = record_pat_field_list .fields() .filter_map(|f| { let ast_pat = f.pat()?; let pat = self.collect_pat(ast_pat, binding_list); let name = f.field_name()?.as_name(); + let src = self.expander.to_source(AstPtr::new(&f)); + self.source_map.pat_field_map_back.insert(pat, src); Some(RecordFieldPat { name, pat }) }) .collect(); - let ellipsis = p - .record_pat_field_list() - .expect("every struct should have a field list") - .rest_pat() - .is_some(); + let ellipsis = record_pat_field_list.rest_pat().is_some(); Pat::Record { path, args, ellipsis } } diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 3423e0f0120..78d3c667a1f 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -194,7 +194,7 @@ pub(crate) type InferResult = Result, TypeError>; #[derive(Debug, PartialEq, Eq, Clone)] pub enum InferenceDiagnostic { NoSuchField { - expr: ExprId, + field: ExprOrPatId, private: bool, }, PrivateField { diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index e283db81fe8..0c3c725a7c7 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -527,7 +527,7 @@ impl InferenceContext<'_> { self.write_variant_resolution(tgt_expr.into(), variant); } match def_id { - Some(_) if fields.is_empty() => {} + _ if fields.is_empty() => {} Some(def) => { let field_types = self.db.field_types(def); let variant_data = def.variant_data(self.db.upcast()); @@ -542,16 +542,16 @@ impl InferenceContext<'_> { ) { self.push_diagnostic( InferenceDiagnostic::NoSuchField { - expr: field.expr, + field: field.expr.into(), private: true, }, ); } - Some(FieldId { parent: def, local_id }) + Some(local_id) } None => { self.push_diagnostic(InferenceDiagnostic::NoSuchField { - expr: field.expr, + field: field.expr.into(), private: false, }); None @@ -559,7 +559,7 @@ impl InferenceContext<'_> { } }; let field_ty = field_def.map_or(self.err_ty(), |it| { - field_types[it.local_id].clone().substitute(Interner, &substs) + field_types[it].clone().substitute(Interner, &substs) }); // Field type might have some unknown types @@ -1154,7 +1154,7 @@ impl InferenceContext<'_> { Expr::Underscore => rhs_ty.clone(), _ => { // `lhs` is a place expression, a unit struct, or an enum variant. - let lhs_ty = self.infer_expr(lhs, &Expectation::none()); + let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none()); // This is the only branch where this function may coerce any type. // We are returning early to avoid the unifiability check below. diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 7d88e42e52a..4e28ec06023 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -92,24 +92,51 @@ impl InferenceContext<'_> { let substs = ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner)); - let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); - let (pre, post) = match ellipsis { - Some(idx) => subs.split_at(idx), - None => (subs, &[][..]), - }; - let post_idx_offset = field_tys.iter().count().saturating_sub(post.len()); + match def { + _ if subs.len() == 0 => {} + Some(def) => { + let field_types = self.db.field_types(def); + let variant_data = def.variant_data(self.db.upcast()); + let visibilities = self.db.field_visibilities(def); - let pre_iter = pre.iter().enumerate(); - let post_iter = (post_idx_offset..).zip(post.iter()); - for (i, &subpat) in pre_iter.chain(post_iter) { - let expected_ty = var_data - .as_ref() - .and_then(|d| d.field(&Name::new_tuple_field(i))) - .map_or(self.err_ty(), |field| { - field_tys[field].clone().substitute(Interner, &substs) - }); - let expected_ty = self.normalize_associated_types_in(expected_ty); - T::infer(self, subpat, &expected_ty, default_bm); + let (pre, post) = match ellipsis { + Some(idx) => subs.split_at(idx), + None => (subs, &[][..]), + }; + let post_idx_offset = field_types.iter().count().saturating_sub(post.len()); + + let pre_iter = pre.iter().enumerate(); + let post_iter = (post_idx_offset..).zip(post.iter()); + + for (i, &subpat) in pre_iter.chain(post_iter) { + let field_def = { + match variant_data.field(&Name::new_tuple_field(i)) { + Some(local_id) => { + if !visibilities[local_id] + .is_visible_from(self.db.upcast(), self.resolver.module()) + { + // FIXME(DIAGNOSE): private tuple field + } + Some(local_id) + } + None => None, + } + }; + + let expected_ty = field_def.map_or(self.err_ty(), |f| { + field_types[f].clone().substitute(Interner, &substs) + }); + let expected_ty = self.normalize_associated_types_in(expected_ty); + + T::infer(self, subpat, &expected_ty, default_bm); + } + } + None => { + let err_ty = self.err_ty(); + for &inner in subs { + T::infer(self, inner, &err_ty, default_bm); + } + } } ty @@ -122,7 +149,7 @@ impl InferenceContext<'_> { expected: &Ty, default_bm: T::BindingMode, id: T, - subs: impl Iterator, + subs: impl Iterator + ExactSizeIterator, ) -> Ty { let (ty, def) = self.resolve_variant(path, false); if let Some(variant) = def { @@ -134,17 +161,51 @@ impl InferenceContext<'_> { let substs = ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner)); - let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); - let var_data = def.map(|it| it.variant_data(self.db.upcast())); + match def { + _ if subs.len() == 0 => {} + Some(def) => { + let field_types = self.db.field_types(def); + let variant_data = def.variant_data(self.db.upcast()); + let visibilities = self.db.field_visibilities(def); - for (name, inner) in subs { - let expected_ty = var_data - .as_ref() - .and_then(|it| it.field(&name)) - .map_or(self.err_ty(), |f| field_tys[f].clone().substitute(Interner, &substs)); - let expected_ty = self.normalize_associated_types_in(expected_ty); + for (name, inner) in subs { + let field_def = { + match variant_data.field(&name) { + Some(local_id) => { + if !visibilities[local_id] + .is_visible_from(self.db.upcast(), self.resolver.module()) + { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: inner.into(), + private: true, + }); + } + Some(local_id) + } + None => { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: inner.into(), + private: false, + }); + None + } + } + }; - T::infer(self, inner, &expected_ty, default_bm); + let expected_ty = field_def.map_or(self.err_ty(), |f| { + field_types[f].clone().substitute(Interner, &substs) + }); + let expected_ty = self.normalize_associated_types_in(expected_ty); + + T::infer(self, inner, &expected_ty, default_bm); + } + } + None => { + let err_ty = self.err_ty(); + for (_, inner) in subs { + T::infer(self, inner, &err_ty, default_bm); + } + } } ty diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index 3ee141b5536..e953058ccca 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -166,8 +166,8 @@ impl ProjectionElem { TyKind::Adt(_, subst) => { db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst) } - _ => { - never!("Only adt has field"); + ty => { + never!("Only adt has field, found {:?}", ty); return TyKind::Error.intern(Interner); } }, diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index d9a6e6fcd55..479138b67f8 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -173,7 +173,7 @@ pub struct MalformedDerive { #[derive(Debug)] pub struct NoSuchField { - pub field: InFile>, + pub field: InFile, AstPtr>>, pub private: bool, } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index ebb8539a66d..453f9b93737 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1503,11 +1503,19 @@ impl DefWithBody { let infer = db.infer(self.into()); let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1); let expr_syntax = |expr| source_map.expr_syntax(expr).expect("unexpected synthetic"); + let pat_syntax = |pat| source_map.pat_syntax(pat).expect("unexpected synthetic"); for d in &infer.diagnostics { match d { - &hir_ty::InferenceDiagnostic::NoSuchField { expr, private } => { - let field = source_map.field_syntax(expr); - acc.push(NoSuchField { field, private }.into()) + &hir_ty::InferenceDiagnostic::NoSuchField { field: expr, private } => { + let expr_or_pat = match expr { + ExprOrPatId::ExprId(expr) => { + source_map.field_syntax(expr).map(Either::Left) + } + ExprOrPatId::PatId(pat) => { + source_map.pat_field_syntax(pat).map(Either::Right) + } + }; + acc.push(NoSuchField { field: expr_or_pat, private }.into()) } &hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { acc.push( @@ -1523,10 +1531,7 @@ impl DefWithBody { &hir_ty::InferenceDiagnostic::PrivateAssocItem { id, item } => { let expr_or_pat = match id { ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left), - ExprOrPatId::PatId(pat) => source_map - .pat_syntax(pat) - .expect("unexpected synthetic") - .map(Either::Right), + ExprOrPatId::PatId(pat) => pat_syntax(pat).map(Either::Right), }; let item = item.into(); acc.push(PrivateAssocItem { expr_or_pat, item }.into()) diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index d5115c2192b..06b03d3d198 100644 --- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -849,6 +849,7 @@ fn main() { struct Foo { } fn main(f: Foo) { match f { Foo { bar } => () } + // ^^^ error: no such field } "#, ); diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs index 34f2b7bbf15..290c16c9d24 100644 --- a/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -1,3 +1,4 @@ +use either::Either; use hir::{db::ExpandDatabase, HasSource, HirDisplay, Semantics}; use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase}; use syntax::{ @@ -12,30 +13,39 @@ use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if created structure does not have field provided in record. pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { - Diagnostic::new_with_syntax_node_ptr( - ctx, - if d.private { - DiagnosticCode::RustcHardError("E0451") - } else { - DiagnosticCode::RustcHardError("E0559") - }, - if d.private { "field is private" } else { "no such field" }, - d.field.clone().map(|it| it.into()), - ) - .with_fixes(fixes(ctx, d)) + let node = d.field.clone().map(|it| it.either(Into::into, Into::into)); + if d.private { + // FIXME: quickfix to add required visibility + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0451"), + "field is private", + node, + ) + } else { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0559"), + "no such field", + node, + ) + .with_fixes(fixes(ctx, d)) + } } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option> { - if d.private { - // FIXME: quickfix to add required visibility - return None; + // FIXME: quickfix for pattern + match &d.field.value { + Either::Left(ptr) => { + let root = ctx.sema.db.parse_or_expand(d.field.file_id); + missing_record_expr_field_fixes( + &ctx.sema, + d.field.file_id.original_file(ctx.sema.db), + &ptr.to_node(&root), + ) + } + _ => None, } - let root = ctx.sema.db.parse_or_expand(d.field.file_id); - missing_record_expr_field_fixes( - &ctx.sema, - d.field.file_id.original_file(ctx.sema.db), - &d.field.value.to_node(&root), - ) } fn missing_record_expr_field_fixes( @@ -126,13 +136,34 @@ mod tests { r#" struct S { foo: i32, bar: () } impl S { - fn new() -> S { + fn new( + s@S { + //^ 💡 error: missing structure fields: + //| - bar + foo, + baz: baz2, + //^^^^^^^^^ error: no such field + qux + //^^^ error: no such field + }: S + ) -> S { + S { + //^ 💡 error: missing structure fields: + //| - bar + foo, + baz: baz2, + //^^^^^^^^^ error: no such field + qux + //^^^ error: no such field + } = s; S { //^ 💡 error: missing structure fields: //| - bar foo: 92, baz: 62, //^^^^^^^ 💡 error: no such field + qux + //^^^ error: no such field } } } @@ -310,13 +341,28 @@ fn main() { r#" mod m { pub struct Struct { - field: u32 + field: u32, + field2: u32, } } -fn main() { +fn f(s@m::Struct { + field: f, + //^^^^^^^^ error: field is private + field2 + //^^^^^^ error: field is private +}: m::Struct) { + // assignee expression m::Struct { - field: 0 + field: 0, //^^^^^^^^ error: field is private + field2 + //^^^^^^ error: field is private + } = s; + m::Struct { + field: 0, + //^^^^^^^^ error: field is private + field2 + //^^^^^^ error: field is private }; } "#, From cfcef69072aa87ecab6f127092972ccab201b7ee Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 9 Sep 2023 14:40:56 +0200 Subject: [PATCH 120/250] shrink_to_fit body source map --- crates/hir-def/src/body.rs | 28 +++++++++++++++++++++++++++- crates/hir-def/src/import_map.rs | 2 +- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 26fd58c0a40..c0baf6011f7 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -166,9 +166,10 @@ impl Body { }; let module = def.module(db); let expander = Expander::new(db, file_id, module); - let (mut body, source_map) = + let (mut body, mut source_map) = Body::new(db, def, expander, params, body, module.krate, is_async_fn); body.shrink_to_fit(); + source_map.shrink_to_fit(); (Arc::new(body), Arc::new(source_map)) } @@ -390,4 +391,29 @@ impl BodySourceMap { pub fn diagnostics(&self) -> &[BodyDiagnostic] { &self.diagnostics } + + fn shrink_to_fit(&mut self) { + let Self { + expr_map, + expr_map_back, + pat_map, + pat_map_back, + label_map, + label_map_back, + field_map_back, + pat_field_map_back, + expansions, + diagnostics, + } = self; + expr_map.shrink_to_fit(); + expr_map_back.shrink_to_fit(); + pat_map.shrink_to_fit(); + pat_map_back.shrink_to_fit(); + label_map.shrink_to_fit(); + label_map_back.shrink_to_fit(); + field_map_back.shrink_to_fit(); + pat_field_map_back.shrink_to_fit(); + expansions.shrink_to_fit(); + diagnostics.shrink_to_fit(); + } } diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index c961d7d86d8..721da29ce81 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -175,7 +175,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap Date: Sun, 10 Sep 2023 00:00:00 +0000 Subject: [PATCH 121/250] Cache reachable_set on disk --- compiler/rustc_middle/src/query/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index bf340846f10..9e358ea4eba 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1140,6 +1140,7 @@ rustc_queries! { query reachable_set(_: ()) -> &'tcx LocalDefIdSet { arena_cache desc { "reachability" } + cache_on_disk_if { true } } /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; From 7db123dfbfe8d50cc83aac693161de8232d37e6e Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 9 Sep 2023 20:20:48 -0400 Subject: [PATCH 122/250] Add myself to the mailmap --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index c072f6282f1..2cc8c5de43f 100644 --- a/.mailmap +++ b/.mailmap @@ -545,6 +545,8 @@ Timothy Maloney Tomas Koutsky Torsten Weber Torsten Weber +Trevor Gross +Trevor Gross Trevor Spiteri Tshepang Mbambo Ty Overby From ccff704c25482c583471312f22f9eecf2052ecc8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 9 Sep 2023 22:45:01 +0200 Subject: [PATCH 123/250] Shrink some stuff --- crates/hir-ty/src/layout.rs | 10 +++++----- crates/hir-ty/src/layout/adt.rs | 2 +- crates/hir-ty/src/layout/tests.rs | 4 ++-- crates/hir-ty/src/mir/borrowck.rs | 1 + crates/hir-ty/src/mir/eval.rs | 8 ++++---- crates/hir-ty/src/mir/lower.rs | 8 ++++---- crates/ide/src/status.rs | 1 + 7 files changed, 18 insertions(+), 16 deletions(-) diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 714930ba667..1a6106c0244 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -24,7 +24,7 @@ pub use self::{ macro_rules! user_error { ($it: expr) => { - return Err(LayoutError::UserError(format!($it))) + return Err(LayoutError::UserError(format!($it).into())) }; } @@ -50,7 +50,7 @@ pub type Variants = hir_def::layout::Variants; #[derive(Debug, PartialEq, Eq, Clone)] pub enum LayoutError { - UserError(String), + UserError(Box), SizeOverflow, TargetLayoutNotAvailable, HasPlaceholder, @@ -234,9 +234,9 @@ pub fn layout_of_ty_query( cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)? } TyKind::Array(element, count) => { - let count = try_const_usize(db, &count).ok_or(LayoutError::UserError( - "unevaluated or mistyped const generic parameter".to_string(), - ))? as u64; + let count = try_const_usize(db, &count).ok_or(LayoutError::UserError(Box::from( + "unevaluated or mistyped const generic parameter", + )))? as u64; let element = db.layout_of_ty(element.clone(), trait_env.clone())?; let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?; diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 1c92e80f335..85ef649b895 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -163,7 +163,7 @@ fn repr_discr( return Err(LayoutError::UserError( "Integer::repr_discr: `#[repr]` hint too small for \ discriminant range of enum " - .to_string(), + .into(), )); } return Ok((discr, ity.is_signed())); diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index 333ad473a8b..ffdbb9de934 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -212,14 +212,14 @@ fn recursive() { } check_fail( r#"struct Goal(Goal);"#, - LayoutError::UserError("infinite sized recursive type".to_string()), + LayoutError::UserError("infinite sized recursive type".into()), ); check_fail( r#" struct Foo(Foo); struct Goal(Foo); "#, - LayoutError::UserError("infinite sized recursive type".to_string()), + LayoutError::UserError("infinite sized recursive type".into()), ); } diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index 7f80c7be80b..41fb129652a 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -179,6 +179,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec None => (), } } + result.shrink_to_fit(); result } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 7d73a4dcaa9..4364e0d323b 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -339,7 +339,7 @@ pub enum MirEvalError { InvalidVTableId(usize), CoerceUnsizedError(Ty), LangItemNotFound(LangItem), - BrokenLayout(Layout), + BrokenLayout(Box), } impl MirEvalError { @@ -408,7 +408,7 @@ impl MirEvalError { err.pretty_print(f, db, span_formatter)?; } MirEvalError::ConstEvalError(name, err) => { - MirLowerError::ConstEvalError(name.clone(), err.clone()).pretty_print( + MirLowerError::ConstEvalError((**name).into(), err.clone()).pretty_print( f, db, span_formatter, @@ -1632,7 +1632,7 @@ impl Evaluator<'_> { if let Some((offset, size, value)) = tag { match result.get_mut(offset..offset + size) { Some(it) => it.copy_from_slice(&value.to_le_bytes()[0..size]), - None => return Err(MirEvalError::BrokenLayout(variant_layout.clone())), + None => return Err(MirEvalError::BrokenLayout(Box::new(variant_layout.clone()))), } } for (i, op) in values.enumerate() { @@ -1640,7 +1640,7 @@ impl Evaluator<'_> { let op = op.get(&self)?; match result.get_mut(offset..offset + op.len()) { Some(it) => it.copy_from_slice(op), - None => return Err(MirEvalError::BrokenLayout(variant_layout.clone())), + None => return Err(MirEvalError::BrokenLayout(Box::new(variant_layout.clone()))), } } Ok(result) diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index bceeb185003..dd2dba717f9 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -71,7 +71,7 @@ struct MirLowerCtx<'a> { #[derive(Debug, Clone, PartialEq, Eq)] pub enum MirLowerError { - ConstEvalError(String, Box), + ConstEvalError(Box, Box), LayoutError(LayoutError), IncompleteExpr, IncompletePattern, @@ -84,7 +84,7 @@ pub enum MirLowerError { UnsizedTemporary(Ty), MissingFunctionDefinition(DefWithBodyId, ExprId), TypeMismatch(TypeMismatch), - /// This should be never happen. Type mismatch should catch everything. + /// This should never happen. Type mismatch should catch everything. TypeError(&'static str), NotSupported(String), ContinueWithoutLoop, @@ -1456,7 +1456,7 @@ impl<'ctx> MirLowerCtx<'ctx> { let name = const_id.name(self.db.upcast()); self.db .const_eval(const_id.into(), subst, None) - .map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))? + .map_err(|e| MirLowerError::ConstEvalError(name.into(), Box::new(e)))? }; Ok(Operand::Constant(c)) } @@ -1853,7 +1853,7 @@ impl<'ctx> MirLowerCtx<'ctx> { data.name.display(self.db.upcast()), data.variants[variant.local_id].name.display(self.db.upcast()) ); - Err(MirLowerError::ConstEvalError(name, Box::new(e))) + Err(MirLowerError::ConstEvalError(name.into(), Box::new(e))) } } } diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs index d2c77e2dc79..c9ee460a1c2 100644 --- a/crates/ide/src/status.rs +++ b/crates/ide/src/status.rs @@ -66,6 +66,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option) -> String { None => format!("{}", krate.into_raw()), }; format_to!(buf, "Crate: {}\n", display_crate(krate)); + format_to!(buf, "Enabled cfgs: {:?}\n", crate_graph[krate].cfg_options); let deps = crate_graph[krate] .dependencies .iter() From 35e0d800f0e7ecad8c4976c0d39d95238f9385a1 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 10 Aug 2023 01:00:12 +0200 Subject: [PATCH 124/250] Deunwrap extract_function --- .../src/handlers/extract_function.rs | 119 +++++++++--------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index ea7a21e77a4..39cd49e6035 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -103,50 +103,49 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let scope = ImportScope::find_insert_use_container(&node, &ctx.sema)?; + let outliving_locals: Vec<_> = ret_values.collect(); + if stdx::never!(!outliving_locals.is_empty() && !ret_ty.is_unit()) { + // We should not have variables that outlive body if we have expression block + return None; + } + + let params = body.extracted_function_params(ctx, &container_info, locals_used.iter().copied()); + + let name = make_function_name(&semantics_scope); + + let has_impl_wrapper = + insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after); + + let fun = Function { + name, + self_param, + params, + control_flow, + ret_ty, + body, + outliving_locals, + contains_tail_expr, + mods: container_info, + }; + + let new_indent = IndentLevel::from_node(&insert_after); + let old_indent = fun.body.indent_level(); + + let fn_def = match fun.self_param_adt(ctx) { + Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => { + let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1)?; + generate_impl_text(&adt, &fn_def).replace("{\n\n", "{") + } + _ => format_function(ctx, module, &fun, old_indent, new_indent)?, + }; + acc.add( AssistId("extract_function", crate::AssistKind::RefactorExtract), "Extract into function", target_range, move |builder| { - let outliving_locals: Vec<_> = ret_values.collect(); - if stdx::never!(!outliving_locals.is_empty() && !ret_ty.is_unit()) { - // We should not have variables that outlive body if we have expression block - return; - } - - let params = - body.extracted_function_params(ctx, &container_info, locals_used.iter().copied()); - - let name = make_function_name(&semantics_scope); - - let fun = Function { - name, - self_param, - params, - control_flow, - ret_ty, - body, - outliving_locals, - contains_tail_expr, - mods: container_info, - }; - - let new_indent = IndentLevel::from_node(&insert_after); - let old_indent = fun.body.indent_level(); - builder.replace(target_range, make_call(ctx, &fun, old_indent)); - let has_impl_wrapper = - insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after); - - let fn_def = match fun.self_param_adt(ctx) { - Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => { - let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1); - generate_impl_text(&adt, &fn_def).replace("{\n\n", "{") - } - _ => format_function(ctx, module, &fun, old_indent, new_indent), - }; - if fn_def.contains("ControlFlow") { let scope = match scope { ImportScope::File(it) => ImportScope::File(builder.make_mut(it)), @@ -1501,13 +1500,13 @@ fn format_function( fun: &Function, old_indent: IndentLevel, new_indent: IndentLevel, -) -> String { +) -> Option { let mut fn_def = String::new(); let fun_name = &fun.name; let params = fun.make_param_list(ctx, module); let ret_ty = fun.make_ret_ty(ctx, module); - let body = make_body(ctx, old_indent, new_indent, fun); + let body = make_body(ctx, old_indent, new_indent, fun)?; let const_kw = if fun.mods.is_const { "const " } else { "" }; let async_kw = if fun.control_flow.is_async { "async " } else { "" }; let unsafe_kw = if fun.control_flow.is_unsafe { "unsafe " } else { "" }; @@ -1535,7 +1534,7 @@ fn format_function( format_to!(fn_def, " {body}"); - fn_def + Some(fn_def) } fn make_generic_params_and_where_clause( @@ -1721,14 +1720,14 @@ fn make_body( old_indent: IndentLevel, new_indent: IndentLevel, fun: &Function, -) -> ast::BlockExpr { +) -> Option { let ret_ty = fun.return_type(ctx); let handler = FlowHandler::from_ret_ty(fun, &ret_ty); let block = match &fun.body { FunctionBody::Expr(expr) => { - let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax()); - let expr = ast::Expr::cast(expr).unwrap(); + let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax())?; + let expr = ast::Expr::cast(expr)?; match expr { ast::Expr::BlockExpr(block) => { // If the extracted expression is itself a block, there is no need to wrap it inside another block. @@ -1749,12 +1748,16 @@ fn make_body( .children_with_tokens() .filter(|it| text_range.contains_range(it.text_range())) .map(|it| match &it { - syntax::NodeOrToken::Node(n) => syntax::NodeOrToken::Node( - rewrite_body_segment(ctx, &fun.params, &handler, n), - ), - _ => it, + syntax::NodeOrToken::Node(n) => { + let bs = rewrite_body_segment(ctx, &fun.params, &handler, n); + match bs { + Some(n) => Some(syntax::NodeOrToken::Node(n)), + None => None, + } + } + _ => Some(it), }) - .collect(); + .collect::>()?; let mut tail_expr = match &elements.last() { Some(syntax::NodeOrToken::Node(node)) if ast::Expr::can_cast(node.kind()) => { @@ -1838,7 +1841,7 @@ fn make_body( }), }; - block.indent(new_indent) + Some(block.indent(new_indent)) } fn map_tail_expr(block: ast::BlockExpr, f: impl FnOnce(ast::Expr) -> ast::Expr) -> ast::BlockExpr { @@ -1896,14 +1899,18 @@ fn rewrite_body_segment( params: &[Param], handler: &FlowHandler, syntax: &SyntaxNode, -) -> SyntaxNode { - let syntax = fix_param_usages(ctx, params, syntax); +) -> Option { + let syntax = fix_param_usages(ctx, params, syntax)?; update_external_control_flow(handler, &syntax); - syntax + Some(syntax) } /// change all usages to account for added `&`/`&mut` for some params -fn fix_param_usages(ctx: &AssistContext<'_>, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { +fn fix_param_usages( + ctx: &AssistContext<'_>, + params: &[Param], + syntax: &SyntaxNode, +) -> Option { let mut usages_for_param: Vec<(&Param, Vec)> = Vec::new(); let tm = TreeMutator::new(syntax); @@ -1934,12 +1941,12 @@ fn fix_param_usages(ctx: &AssistContext<'_>, params: &[Param], syntax: &SyntaxNo Some(ast::Expr::RefExpr(node)) if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => { - ted::replace(node.syntax(), node.expr().unwrap().syntax()); + ted::replace(node.syntax(), node.expr()?.syntax()); } Some(ast::Expr::RefExpr(node)) if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => { - ted::replace(node.syntax(), node.expr().unwrap().syntax()); + ted::replace(node.syntax(), node.expr()?.syntax()); } Some(_) | None => { let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update(); @@ -1949,7 +1956,7 @@ fn fix_param_usages(ctx: &AssistContext<'_>, params: &[Param], syntax: &SyntaxNo } } - res + Some(res) } fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) { From 6f7460484a2abe4a458c53b0e02668e0af457377 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Tue, 15 Aug 2023 23:59:29 +0200 Subject: [PATCH 125/250] v2 --- .../src/handlers/extract_function.rs | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 39cd49e6035..8ef43e4f9a3 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -1726,8 +1726,9 @@ fn make_body( let block = match &fun.body { FunctionBody::Expr(expr) => { - let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax())?; - let expr = ast::Expr::cast(expr)?; + let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax()) + .expect("rewrite_body_segment failed."); + let expr = ast::Expr::cast(expr).expect("error while casting body segment to an expr."); match expr { ast::Expr::BlockExpr(block) => { // If the extracted expression is itself a block, there is no need to wrap it inside another block. @@ -1934,25 +1935,22 @@ fn fix_param_usages( for (param, usages) in usages_for_param { for usage in usages { - match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { - Some(ast::Expr::MethodCallExpr(_) | ast::Expr::FieldExpr(_)) => { - // do nothing - } - Some(ast::Expr::RefExpr(node)) - if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => + let expr = usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast); + if let Some(ast::Expr::MethodCallExpr(_) | ast::Expr::FieldExpr(_)) = expr { + continue; + } + + if let Some(ast::Expr::RefExpr(node)) = expr { + if (param.kind() == ParamKind::MutRef && node.mut_token().is_some()) + || (param.kind() == ParamKind::SharedRef && node.mut_token().is_none()) { ted::replace(node.syntax(), node.expr()?.syntax()); - } - Some(ast::Expr::RefExpr(node)) - if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => - { - ted::replace(node.syntax(), node.expr()?.syntax()); - } - Some(_) | None => { - let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update(); - ted::replace(usage.syntax(), p.syntax()) + continue; } } + + let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update(); + ted::replace(usage.syntax(), p.syntax()) } } From 0f1673c6f1750fd32b584575ebe2a8098e56e714 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sun, 10 Sep 2023 23:00:19 +0200 Subject: [PATCH 126/250] v3 --- .../src/handlers/extract_function.rs | 182 +++++++++--------- 1 file changed, 88 insertions(+), 94 deletions(-) diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 8ef43e4f9a3..de591cfde94 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -103,49 +103,50 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let scope = ImportScope::find_insert_use_container(&node, &ctx.sema)?; - let outliving_locals: Vec<_> = ret_values.collect(); - if stdx::never!(!outliving_locals.is_empty() && !ret_ty.is_unit()) { - // We should not have variables that outlive body if we have expression block - return None; - } - - let params = body.extracted_function_params(ctx, &container_info, locals_used.iter().copied()); - - let name = make_function_name(&semantics_scope); - - let has_impl_wrapper = - insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after); - - let fun = Function { - name, - self_param, - params, - control_flow, - ret_ty, - body, - outliving_locals, - contains_tail_expr, - mods: container_info, - }; - - let new_indent = IndentLevel::from_node(&insert_after); - let old_indent = fun.body.indent_level(); - - let fn_def = match fun.self_param_adt(ctx) { - Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => { - let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1)?; - generate_impl_text(&adt, &fn_def).replace("{\n\n", "{") - } - _ => format_function(ctx, module, &fun, old_indent, new_indent)?, - }; - acc.add( AssistId("extract_function", crate::AssistKind::RefactorExtract), "Extract into function", target_range, move |builder| { + let outliving_locals: Vec<_> = ret_values.collect(); + if stdx::never!(!outliving_locals.is_empty() && !ret_ty.is_unit()) { + // We should not have variables that outlive body if we have expression block + return; + } + + let params = + body.extracted_function_params(ctx, &container_info, locals_used.iter().copied()); + + let name = make_function_name(&semantics_scope); + + let fun = Function { + name, + self_param, + params, + control_flow, + ret_ty, + body, + outliving_locals, + contains_tail_expr, + mods: container_info, + }; + + let new_indent = IndentLevel::from_node(&insert_after); + let old_indent = fun.body.indent_level(); + builder.replace(target_range, make_call(ctx, &fun, old_indent)); + let has_impl_wrapper = + insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after); + + let fn_def = match fun.self_param_adt(ctx) { + Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => { + let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1); + generate_impl_text(&adt, &fn_def).replace("{\n\n", "{") + } + _ => format_function(ctx, module, &fun, old_indent, new_indent), + }; + if fn_def.contains("ControlFlow") { let scope = match scope { ImportScope::File(it) => ImportScope::File(builder.make_mut(it)), @@ -530,7 +531,7 @@ impl FunctionBody { fn extracted_from_trait_impl(&self) -> bool { match self.node().ancestors().find_map(ast::Impl::cast) { - Some(c) => return c.trait_().is_some(), + Some(c) => c.trait_().is_some(), None => false, } } @@ -1047,23 +1048,17 @@ impl GenericParent { fn generic_parents(parent: &SyntaxNode) -> Vec { let mut list = Vec::new(); if let Some(parent_item) = parent.ancestors().find_map(ast::Item::cast) { - match parent_item { - ast::Item::Fn(ref fn_) => { - if let Some(parent_parent) = parent_item - .syntax() - .parent() - .and_then(|it| it.parent()) - .and_then(ast::Item::cast) - { - match parent_parent { - ast::Item::Impl(impl_) => list.push(GenericParent::Impl(impl_)), - ast::Item::Trait(trait_) => list.push(GenericParent::Trait(trait_)), - _ => (), - } + if let ast::Item::Fn(ref fn_) = parent_item { + if let Some(parent_parent) = + parent_item.syntax().parent().and_then(|it| it.parent()).and_then(ast::Item::cast) + { + match parent_parent { + ast::Item::Impl(impl_) => list.push(GenericParent::Impl(impl_)), + ast::Item::Trait(trait_) => list.push(GenericParent::Trait(trait_)), + _ => (), } - list.push(GenericParent::Fn(fn_.clone())); } - _ => (), + list.push(GenericParent::Fn(fn_.clone())); } } list @@ -1500,13 +1495,13 @@ fn format_function( fun: &Function, old_indent: IndentLevel, new_indent: IndentLevel, -) -> Option { +) -> String { let mut fn_def = String::new(); let fun_name = &fun.name; let params = fun.make_param_list(ctx, module); let ret_ty = fun.make_ret_ty(ctx, module); - let body = make_body(ctx, old_indent, new_indent, fun)?; + let body = make_body(ctx, old_indent, new_indent, fun); let const_kw = if fun.mods.is_const { "const " } else { "" }; let async_kw = if fun.control_flow.is_async { "async " } else { "" }; let unsafe_kw = if fun.control_flow.is_unsafe { "unsafe " } else { "" }; @@ -1534,7 +1529,7 @@ fn format_function( format_to!(fn_def, " {body}"); - Some(fn_def) + fn_def } fn make_generic_params_and_where_clause( @@ -1720,15 +1715,14 @@ fn make_body( old_indent: IndentLevel, new_indent: IndentLevel, fun: &Function, -) -> Option { +) -> ast::BlockExpr { let ret_ty = fun.return_type(ctx); let handler = FlowHandler::from_ret_ty(fun, &ret_ty); let block = match &fun.body { FunctionBody::Expr(expr) => { - let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax()) - .expect("rewrite_body_segment failed."); - let expr = ast::Expr::cast(expr).expect("error while casting body segment to an expr."); + let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax()); + let expr = ast::Expr::cast(expr).expect("Body segment should be an expr"); match expr { ast::Expr::BlockExpr(block) => { // If the extracted expression is itself a block, there is no need to wrap it inside another block. @@ -1749,16 +1743,12 @@ fn make_body( .children_with_tokens() .filter(|it| text_range.contains_range(it.text_range())) .map(|it| match &it { - syntax::NodeOrToken::Node(n) => { - let bs = rewrite_body_segment(ctx, &fun.params, &handler, n); - match bs { - Some(n) => Some(syntax::NodeOrToken::Node(n)), - None => None, - } - } - _ => Some(it), + syntax::NodeOrToken::Node(n) => syntax::NodeOrToken::Node( + rewrite_body_segment(ctx, &fun.params, &handler, n), + ), + _ => it, }) - .collect::>()?; + .collect(); let mut tail_expr = match &elements.last() { Some(syntax::NodeOrToken::Node(node)) if ast::Expr::can_cast(node.kind()) => { @@ -1842,7 +1832,7 @@ fn make_body( }), }; - Some(block.indent(new_indent)) + block.indent(new_indent) } fn map_tail_expr(block: ast::BlockExpr, f: impl FnOnce(ast::Expr) -> ast::Expr) -> ast::BlockExpr { @@ -1872,9 +1862,8 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr if let Some(stmt_list) = block.stmt_list() { stmt_list.syntax().children_with_tokens().for_each(|node_or_token| { - match &node_or_token { - syntax::NodeOrToken::Token(_) => elements.push(node_or_token), - _ => (), + if let syntax::NodeOrToken::Token(_) = &node_or_token { + elements.push(node_or_token) }; }); } @@ -1900,18 +1889,14 @@ fn rewrite_body_segment( params: &[Param], handler: &FlowHandler, syntax: &SyntaxNode, -) -> Option { - let syntax = fix_param_usages(ctx, params, syntax)?; +) -> SyntaxNode { + let syntax = fix_param_usages(ctx, params, syntax); update_external_control_flow(handler, &syntax); - Some(syntax) + syntax } /// change all usages to account for added `&`/`&mut` for some params -fn fix_param_usages( - ctx: &AssistContext<'_>, - params: &[Param], - syntax: &SyntaxNode, -) -> Option { +fn fix_param_usages(ctx: &AssistContext<'_>, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { let mut usages_for_param: Vec<(&Param, Vec)> = Vec::new(); let tm = TreeMutator::new(syntax); @@ -1935,26 +1920,35 @@ fn fix_param_usages( for (param, usages) in usages_for_param { for usage in usages { - let expr = usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast); - if let Some(ast::Expr::MethodCallExpr(_) | ast::Expr::FieldExpr(_)) = expr { - continue; - } - - if let Some(ast::Expr::RefExpr(node)) = expr { - if (param.kind() == ParamKind::MutRef && node.mut_token().is_some()) - || (param.kind() == ParamKind::SharedRef && node.mut_token().is_none()) + match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { + Some(ast::Expr::MethodCallExpr(_) | ast::Expr::FieldExpr(_)) => { + // do nothing + } + Some(ast::Expr::RefExpr(node)) + if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => { - ted::replace(node.syntax(), node.expr()?.syntax()); - continue; + ted::replace( + node.syntax(), + node.expr().expect("RefExpr::expr() cannot be None").syntax(), + ); + } + Some(ast::Expr::RefExpr(node)) + if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => + { + ted::replace( + node.syntax(), + node.expr().expect("RefExpr::expr() cannot be None").syntax(), + ); + } + Some(_) | None => { + let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update(); + ted::replace(usage.syntax(), p.syntax()) } } - - let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update(); - ted::replace(usage.syntax(), p.syntax()) } } - Some(res) + res } fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) { From 7ae70a06ceb9b6a2619bb91f8d839417c3ee3f67 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sat, 8 Jul 2023 02:39:03 +0200 Subject: [PATCH 127/250] Disallow renaming of non-local structs --- crates/ide-db/src/rename.rs | 11 ++++++++++- crates/ide/src/rename.rs | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index aa0bb7cce69..27add0ad371 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -22,7 +22,7 @@ //! Our current behavior is ¯\_(ツ)_/¯. use std::fmt; -use base_db::{AnchoredPathBuf, FileId, FileRange}; +use base_db::{AnchoredPathBuf, CrateOrigin, FileId, FileRange}; use either::Either; use hir::{FieldSource, HasSource, InFile, ModuleSource, Semantics}; use stdx::never; @@ -77,6 +77,15 @@ impl Definition { bail!("Cannot rename builtin type") } Definition::SelfType(_) => bail!("Cannot rename `Self`"), + Definition::Adt(hir::Adt::Struct(strukt)) => { + if !matches!( + strukt.module(sema.db).krate().origin(sema.db), + CrateOrigin::Local { .. } + ) { + bail!("Cannot rename a non-local struct.") + } + rename_reference(sema, Definition::Adt(hir::Adt::Struct(strukt)), new_name) + } def => rename_reference(sema, def, new_name), } } diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index dae8e71e8a0..3017ca75366 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -2529,6 +2529,7 @@ fn main() { ", ) } +<<<<<<< HEAD #[test] fn extern_crate() { @@ -2634,4 +2635,21 @@ use qux as frob; // ", // ); } +||||||| parent of 948d9f274 (Disallow renaming of non-local structs) +======= + + #[test] + fn disallow_renaming_for_non_local_struct() { + check( + "Baz", + r#" +//- /lib.rs crate:lib new_source_root:library +pub struct S$0; +//- /main.rs crate:main deps:lib new_source_root:local +use lib::S; +"#, + "error: Cannot rename a non-local struct.", + ); + } +>>>>>>> 948d9f274 (Disallow renaming of non-local structs) } From 43edb51b21e94662f61398079c8d30f19641561a Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sat, 8 Jul 2023 12:22:07 +0200 Subject: [PATCH 128/250] Generalize disallowing of definition renaming --- crates/ide-db/src/rename.rs | 18 +++++++++++------- crates/ide/src/rename.rs | 4 ++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 27add0ad371..adb9e55613f 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -71,20 +71,24 @@ impl Definition { sema: &Semantics<'_, RootDatabase>, new_name: &str, ) -> Result { + if let Some(krate) = self.krate(sema.db) { + if !matches!(krate.origin(sema.db), CrateOrigin::Local { .. }) { + bail!("Cannot rename a non-local definition.") + } + } + match *self { Definition::Module(module) => rename_mod(sema, module, new_name), Definition::BuiltinType(_) => { bail!("Cannot rename builtin type") } Definition::SelfType(_) => bail!("Cannot rename `Self`"), - Definition::Adt(hir::Adt::Struct(strukt)) => { - if !matches!( - strukt.module(sema.db).krate().origin(sema.db), - CrateOrigin::Local { .. } - ) { - bail!("Cannot rename a non-local struct.") + Definition::Macro(mac) => { + if mac.is_builtin_derive(sema.db) { + bail!("Cannot rename builtin macro") } - rename_reference(sema, Definition::Adt(hir::Adt::Struct(strukt)), new_name) + + rename_reference(sema, Definition::Macro(mac), new_name) } def => rename_reference(sema, def, new_name), } diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 3017ca75366..5a6742d5c84 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -2639,7 +2639,7 @@ use qux as frob; ======= #[test] - fn disallow_renaming_for_non_local_struct() { + fn disallow_renaming_for_non_local_definition() { check( "Baz", r#" @@ -2648,7 +2648,7 @@ pub struct S$0; //- /main.rs crate:main deps:lib new_source_root:local use lib::S; "#, - "error: Cannot rename a non-local struct.", + "error: Cannot rename a non-local definition.", ); } >>>>>>> 948d9f274 (Disallow renaming of non-local structs) From 6dc7fa94235f1b2ca9ecd8bf90a96c7c1871f111 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 24 Aug 2023 02:01:07 +0200 Subject: [PATCH 129/250] v3 --- crates/ide-db/src/rename.rs | 14 +++++++++++--- crates/ide/src/rename.rs | 23 +++++++++++++++++------ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index adb9e55613f..a60b2423595 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -22,7 +22,7 @@ //! Our current behavior is ¯\_(ツ)_/¯. use std::fmt; -use base_db::{AnchoredPathBuf, CrateOrigin, FileId, FileRange}; +use base_db::{AnchoredPathBuf, FileId, FileRange}; use either::Either; use hir::{FieldSource, HasSource, InFile, ModuleSource, Semantics}; use stdx::never; @@ -71,21 +71,29 @@ impl Definition { sema: &Semantics<'_, RootDatabase>, new_name: &str, ) -> Result { + // self.krate() returns None if + // self is a built-in attr, built-in type or tool module. if let Some(krate) = self.krate(sema.db) { - if !matches!(krate.origin(sema.db), CrateOrigin::Local { .. }) { + if !krate.origin(sema.db).is_local() { bail!("Cannot rename a non-local definition.") } } match *self { Definition::Module(module) => rename_mod(sema, module, new_name), + Definition::ToolModule(_) => { + bail!("Cannot rename a tool module") + } Definition::BuiltinType(_) => { bail!("Cannot rename builtin type") } + Definition::BuiltinAttr(_) => { + bail!("Cannot rename a builtin attr.") + } Definition::SelfType(_) => bail!("Cannot rename `Self`"), Definition::Macro(mac) => { if mac.is_builtin_derive(sema.db) { - bail!("Cannot rename builtin macro") + bail!("Cannot rename builtin derive") } rename_reference(sema, Definition::Macro(mac), new_name) diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 5a6742d5c84..ac9df5ed6d1 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -2529,7 +2529,6 @@ fn main() { ", ) } -<<<<<<< HEAD #[test] fn extern_crate() { @@ -2635,8 +2634,6 @@ use qux as frob; // ", // ); } -||||||| parent of 948d9f274 (Disallow renaming of non-local structs) -======= #[test] fn disallow_renaming_for_non_local_definition() { @@ -2644,12 +2641,26 @@ use qux as frob; "Baz", r#" //- /lib.rs crate:lib new_source_root:library -pub struct S$0; +pub struct S; //- /main.rs crate:main deps:lib new_source_root:local -use lib::S; +use lib::S$0; "#, "error: Cannot rename a non-local definition.", ); } ->>>>>>> 948d9f274 (Disallow renaming of non-local structs) + + #[test] + fn disallow_renaming_for_builtin_macros() { + check( + "Baz", + r#" +//- minicore: derive, hash +//- /main.rs crate:main +use core::hash::Hash; +#[derive(H$0ash)] +struct A; + "#, + "error: Cannot rename a non-local definition.", + ) + } } From a0c8bee35edcfb16c2c92f5c61bc574d6e555a44 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Thu, 24 Aug 2023 02:05:14 +0200 Subject: [PATCH 130/250] Add more comments as requested --- crates/ide-db/src/rename.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index a60b2423595..f6de3a88848 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -73,6 +73,8 @@ impl Definition { ) -> Result { // self.krate() returns None if // self is a built-in attr, built-in type or tool module. + // it is not allowed for these defs to be renamed. + // cases where self.krate() is None is handled below. if let Some(krate) = self.krate(sema.db) { if !krate.origin(sema.db).is_local() { bail!("Cannot rename a non-local definition.") From 0118741632f9999da1db1071ae1a9b430d438d43 Mon Sep 17 00:00:00 2001 From: Ali Bektas Date: Sun, 10 Sep 2023 23:25:36 +0200 Subject: [PATCH 131/250] v4 --- crates/ide-db/src/rename.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index f6de3a88848..353a9749a37 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -93,13 +93,7 @@ impl Definition { bail!("Cannot rename a builtin attr.") } Definition::SelfType(_) => bail!("Cannot rename `Self`"), - Definition::Macro(mac) => { - if mac.is_builtin_derive(sema.db) { - bail!("Cannot rename builtin derive") - } - - rename_reference(sema, Definition::Macro(mac), new_name) - } + Definition::Macro(mac) => rename_reference(sema, Definition::Macro(mac), new_name), def => rename_reference(sema, def, new_name), } } From cd0a89ac4f5e8b7399ee08f0f41f34d1a84cbd3f Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Mon, 11 Sep 2023 10:53:28 -0700 Subject: [PATCH 132/250] fix: field shorthand overwritten in promote local to const assist --- .../src/handlers/promote_local_to_const.rs | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/crates/ide-assists/src/handlers/promote_local_to_const.rs b/crates/ide-assists/src/handlers/promote_local_to_const.rs index 6fc2aa49974..6ed9bd85fcc 100644 --- a/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -76,12 +76,19 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) let name = to_upper_snake_case(&name.to_string()); let usages = Definition::Local(local).usages(&ctx.sema).all(); if let Some(usages) = usages.references.get(&ctx.file_id()) { - let name = make::name_ref(&name); + let name_ref = make::name_ref(&name); for usage in usages { let Some(usage) = usage.name.as_name_ref().cloned() else { continue }; - let usage = edit.make_mut(usage); - ted::replace(usage.syntax(), name.clone_for_update().syntax()); + if let Some(record_field) = ast::RecordExprField::for_name_ref(&usage) { + let record_field = edit.make_mut(record_field); + let name_expr = + make::expr_path(make::path_from_text(&name)).clone_for_update(); + record_field.replace_expr(name_expr); + } else { + let usage = edit.make_mut(usage); + ted::replace(usage.syntax(), name_ref.clone_for_update().syntax()); + } } } @@ -178,6 +185,33 @@ fn foo() { ); } + #[test] + fn usage_in_field_shorthand() { + check_assist( + promote_local_to_const, + r" +struct Foo { + bar: usize, +} + +fn main() { + let $0bar = 0; + let foo = Foo { bar }; +} +", + r" +struct Foo { + bar: usize, +} + +fn main() { + const $0BAR: usize = 0; + let foo = Foo { bar: BAR }; +} +", + ) + } + #[test] fn not_applicable_non_const_meth_call() { cov_mark::check!(promote_local_non_const); From 8c5bc990cc74287bf8c6c2d52bea5f643452baa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sun, 10 Sep 2023 13:56:23 +0200 Subject: [PATCH 133/250] Store a index per dep node kind --- .../src/dep_graph/serialized.rs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 4ba0cb31d0b..f689ed75168 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -46,6 +46,7 @@ use rustc_data_structures::sync::Lock; use rustc_index::{Idx, IndexVec}; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; +use std::iter; use std::marker::PhantomData; // The maximum value of `SerializedDepNodeIndex` leaves the upper two bits @@ -81,8 +82,9 @@ pub struct SerializedDepGraph { /// A flattened list of all edge targets in the graph, stored in the same /// varint encoding that we use on disk. Edge sources are implicit in edge_list_indices. edge_list_data: Vec, - /// Reciprocal map to `nodes`. - index: FxHashMap, SerializedDepNodeIndex>, + /// Stores a map from fingerprints to nodes per dep node kind. + /// This is the reciprocal of `nodes`. + index: Vec>, } impl Default for SerializedDepGraph { @@ -137,7 +139,7 @@ impl SerializedDepGraph { #[inline] pub fn node_to_index_opt(&self, dep_node: &DepNode) -> Option { - self.index.get(dep_node).cloned() + self.index.get(dep_node.kind.to_u16() as usize)?.get(&dep_node.hash).cloned() } #[inline] @@ -147,7 +149,7 @@ impl SerializedDepGraph { #[inline] pub fn node_count(&self) -> usize { - self.index.len() + self.nodes.len() } } @@ -220,7 +222,8 @@ impl<'a, K: DepKind + Decodable>> Decodable> for _index in 0..node_count { // Decode the header for this edge; the header packs together as many of the fixed-size // fields as possible to limit the number of times we update decoder state. - let node_header = SerializedNodeHeader { bytes: d.read_array(), _marker: PhantomData }; + let node_header = + SerializedNodeHeader:: { bytes: d.read_array(), _marker: PhantomData }; let _i: SerializedDepNodeIndex = nodes.push(node_header.node()); debug_assert_eq!(_i.index(), _index); @@ -251,8 +254,12 @@ impl<'a, K: DepKind + Decodable>> Decodable> // end of the array. This padding ensure it doesn't. edge_list_data.extend(&[0u8; DEP_NODE_PAD]); - let index: FxHashMap<_, _> = - nodes.iter_enumerated().map(|(idx, &dep_node)| (dep_node, idx)).collect(); + let mut index: Vec<_> = + iter::repeat(FxHashMap::default()).take(K::MAX as usize + 1).collect(); + + for (idx, node) in nodes.iter_enumerated() { + index[node.kind.to_u16() as usize].insert(node.hash, idx); + } SerializedDepGraph { nodes, fingerprints, edge_list_indices, edge_list_data, index } } From 734e5a1fbd9a1a6bd0dd3058d0b428f277719cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Mon, 11 Sep 2023 17:09:28 +0200 Subject: [PATCH 134/250] Encode the number of dep kinds encountered in the dep graph --- .../src/dep_graph/serialized.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index f689ed75168..b3d64bb85f0 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -254,8 +254,10 @@ impl<'a, K: DepKind + Decodable>> Decodable> // end of the array. This padding ensure it doesn't. edge_list_data.extend(&[0u8; DEP_NODE_PAD]); - let mut index: Vec<_> = - iter::repeat(FxHashMap::default()).take(K::MAX as usize + 1).collect(); + // Read the number of each dep kind and use it to create an hash map with a suitable size. + let mut index: Vec<_> = (0..(K::MAX as usize + 1)) + .map(|_| FxHashMap::with_capacity_and_hasher(d.read_u32() as usize, Default::default())) + .collect(); for (idx, node) in nodes.iter_enumerated() { index[node.kind.to_u16() as usize].insert(node.hash, idx); @@ -426,6 +428,9 @@ struct EncoderState { total_node_count: usize, total_edge_count: usize, stats: Option>>, + + /// Stores the number of times we've encoded each dep kind. + kind_stats: Vec, } impl EncoderState { @@ -435,6 +440,7 @@ impl EncoderState { total_edge_count: 0, total_node_count: 0, stats: record_stats.then(FxHashMap::default), + kind_stats: iter::repeat(0).take(K::MAX as usize + 1).collect(), } } @@ -445,6 +451,7 @@ impl EncoderState { ) -> DepNodeIndex { let index = DepNodeIndex::new(self.total_node_count); self.total_node_count += 1; + self.kind_stats[node.node.kind.to_u16() as usize] += 1; let edge_count = node.edges.len(); self.total_edge_count += edge_count; @@ -470,11 +477,16 @@ impl EncoderState { } fn finish(self, profiler: &SelfProfilerRef) -> FileEncodeResult { - let Self { mut encoder, total_node_count, total_edge_count, stats: _ } = self; + let Self { mut encoder, total_node_count, total_edge_count, stats: _, kind_stats } = self; let node_count = total_node_count.try_into().unwrap(); let edge_count = total_edge_count.try_into().unwrap(); + // Encode the number of each dep kind encountered + for count in kind_stats.iter() { + count.encode(&mut encoder); + } + debug!(?node_count, ?edge_count); debug!("position: {:?}", encoder.position()); IntEncodedWithFixedSize(node_count).encode(&mut encoder); From f8ad88be81257c3be04a4ff1f18bd9277f7b04cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 12 Sep 2023 08:59:37 +0200 Subject: [PATCH 135/250] Use `UnhashMap` for the index --- compiler/rustc_query_system/src/dep_graph/serialized.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index b3d64bb85f0..213e5c8ba68 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -43,6 +43,7 @@ use rustc_data_structures::fingerprint::PackedFingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::Lock; +use rustc_data_structures::unhash::UnhashMap; use rustc_index::{Idx, IndexVec}; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -84,7 +85,7 @@ pub struct SerializedDepGraph { edge_list_data: Vec, /// Stores a map from fingerprints to nodes per dep node kind. /// This is the reciprocal of `nodes`. - index: Vec>, + index: Vec>, } impl Default for SerializedDepGraph { @@ -256,7 +257,7 @@ impl<'a, K: DepKind + Decodable>> Decodable> // Read the number of each dep kind and use it to create an hash map with a suitable size. let mut index: Vec<_> = (0..(K::MAX as usize + 1)) - .map(|_| FxHashMap::with_capacity_and_hasher(d.read_u32() as usize, Default::default())) + .map(|_| UnhashMap::with_capacity_and_hasher(d.read_u32() as usize, Default::default())) .collect(); for (idx, node) in nodes.iter_enumerated() { From 814f4f6f526b302bda9a464130b97502e39cc490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Tue, 16 May 2023 17:10:25 +0200 Subject: [PATCH 136/250] Improve `PadAdapter::write_char` --- library/core/src/fmt/builders.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index d2c9f980042..9227248041e 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -40,6 +40,14 @@ impl fmt::Write for PadAdapter<'_, '_> { Ok(()) } + + fn write_char(&mut self, c: char) -> fmt::Result { + if self.state.on_newline { + self.buf.write_str(" ")?; + } + self.state.on_newline = c == '\n'; + self.buf.write_char(c) + } } /// A struct to help with [`fmt::Debug`](Debug) implementations. From 074fb2c6b7c4c7fb1de7d303a37632c02dd10cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 12 Sep 2023 18:02:45 +0200 Subject: [PATCH 137/250] Store target triple in environment --- src/tools/opt-dist/src/environment/linux.rs | 14 +++++++++++++- src/tools/opt-dist/src/environment/mod.rs | 10 ++++------ src/tools/opt-dist/src/environment/windows.rs | 9 +++++++-- src/tools/opt-dist/src/main.rs | 4 +++- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/tools/opt-dist/src/environment/linux.rs b/src/tools/opt-dist/src/environment/linux.rs index 58b7e6d2306..bd8dd037ae5 100644 --- a/src/tools/opt-dist/src/environment/linux.rs +++ b/src/tools/opt-dist/src/environment/linux.rs @@ -3,9 +3,21 @@ use crate::exec::cmd; use crate::utils::io::copy_directory; use camino::{Utf8Path, Utf8PathBuf}; -pub(super) struct LinuxEnvironment; +pub(super) struct LinuxEnvironment { + target_triple: String, +} + +impl LinuxEnvironment { + pub fn new(target_triple: String) -> Self { + Self { target_triple } + } +} impl Environment for LinuxEnvironment { + fn host_triple(&self) -> &str { + &self.target_triple + } + fn python_binary(&self) -> &'static str { "python3" } diff --git a/src/tools/opt-dist/src/environment/mod.rs b/src/tools/opt-dist/src/environment/mod.rs index a8650fad011..9f7f31d4791 100644 --- a/src/tools/opt-dist/src/environment/mod.rs +++ b/src/tools/opt-dist/src/environment/mod.rs @@ -6,9 +6,7 @@ mod linux; mod windows; pub trait Environment { - fn host_triple(&self) -> String { - std::env::var("PGO_HOST").expect("PGO_HOST environment variable missing") - } + fn host_triple(&self) -> &str; fn python_binary(&self) -> &'static str; @@ -69,9 +67,9 @@ pub trait Environment { fn skipped_tests(&self) -> &'static [&'static str]; } -pub fn create_environment() -> Box { +pub fn create_environment(target_triple: String) -> Box { #[cfg(target_family = "unix")] - return Box::new(linux::LinuxEnvironment); + return Box::new(linux::LinuxEnvironment::new(target_triple)); #[cfg(target_family = "windows")] - return Box::new(windows::WindowsEnvironment::new()); + return Box::new(windows::WindowsEnvironment::new(target_triple)); } diff --git a/src/tools/opt-dist/src/environment/windows.rs b/src/tools/opt-dist/src/environment/windows.rs index 79399391798..90ddc01e893 100644 --- a/src/tools/opt-dist/src/environment/windows.rs +++ b/src/tools/opt-dist/src/environment/windows.rs @@ -9,15 +9,20 @@ use zip::ZipArchive; pub(super) struct WindowsEnvironment { checkout_dir: Utf8PathBuf, + target_triple: String, } impl WindowsEnvironment { - pub fn new() -> Self { - Self { checkout_dir: std::env::current_dir().unwrap().try_into().unwrap() } + pub fn new(target_triple: String) -> Self { + Self { checkout_dir: std::env::current_dir().unwrap().try_into().unwrap(), target_triple } } } impl Environment for WindowsEnvironment { + fn host_triple(&self) -> &str { + &self.target_triple + } + fn python_binary(&self) -> &'static str { "python" } diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index 8ab19674d05..484ca5b3b86 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -171,6 +171,8 @@ fn main() -> anyhow::Result<()> { .parse_default_env() .init(); + let target_triple = std::env::var("PGO_HOST").expect("PGO_HOST environment variable missing"); + let mut build_args: Vec = std::env::args().skip(1).collect(); println!("Running optimized build pipeline with args `{}`", build_args.join(" ")); @@ -202,7 +204,7 @@ fn main() -> anyhow::Result<()> { } let mut timer = Timer::new(); - let env = create_environment(); + let env = create_environment(target_triple); let result = execute_pipeline(env.as_ref(), &mut timer, build_args); log::info!("Timer results\n{}", timer.format_stats()); From 95500f494118b74c965a94acce3e92a803ee8e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 12 Sep 2023 18:07:31 +0200 Subject: [PATCH 138/250] Make executable extension platform, rather than environment dependent --- src/tools/opt-dist/src/environment/linux.rs | 4 ---- src/tools/opt-dist/src/environment/mod.rs | 20 +++++++++++++------ src/tools/opt-dist/src/environment/windows.rs | 4 ---- src/tools/opt-dist/src/tests.rs | 8 ++++---- src/tools/opt-dist/src/training.rs | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/tools/opt-dist/src/environment/linux.rs b/src/tools/opt-dist/src/environment/linux.rs index bd8dd037ae5..61f05137298 100644 --- a/src/tools/opt-dist/src/environment/linux.rs +++ b/src/tools/opt-dist/src/environment/linux.rs @@ -57,10 +57,6 @@ impl Environment for LinuxEnvironment { true } - fn executable_extension(&self) -> &'static str { - "" - } - fn skipped_tests(&self) -> &'static [&'static str] { &[ // Fails because of linker errors, as of June 2023. diff --git a/src/tools/opt-dist/src/environment/mod.rs b/src/tools/opt-dist/src/environment/mod.rs index 9f7f31d4791..271b41316ab 100644 --- a/src/tools/opt-dist/src/environment/mod.rs +++ b/src/tools/opt-dist/src/environment/mod.rs @@ -31,21 +31,21 @@ pub trait Environment { self.build_artifacts() .join("stage0") .join("bin") - .join(format!("cargo{}", self.executable_extension())) + .join(format!("cargo{}", executable_extension())) } fn rustc_stage_0(&self) -> Utf8PathBuf { self.build_artifacts() .join("stage0") .join("bin") - .join(format!("rustc{}", self.executable_extension())) + .join(format!("rustc{}", executable_extension())) } fn rustc_stage_2(&self) -> Utf8PathBuf { self.build_artifacts() .join("stage2") .join("bin") - .join(format!("rustc{}", self.executable_extension())) + .join(format!("rustc{}", executable_extension())) } /// Path to the built rustc-perf benchmark suite. @@ -60,9 +60,6 @@ pub trait Environment { fn supports_shared_llvm(&self) -> bool; - /// What is the extension of binary executables in this environment? - fn executable_extension(&self) -> &'static str; - /// List of test paths that should be skipped when testing the optimized artifacts. fn skipped_tests(&self) -> &'static [&'static str]; } @@ -73,3 +70,14 @@ pub fn create_environment(target_triple: String) -> Box { #[cfg(target_family = "windows")] return Box::new(windows::WindowsEnvironment::new(target_triple)); } + +/// What is the extension of binary executables on this platform? +#[cfg(target_family = "unix")] +pub fn executable_extension() -> &'static str { + "" +} + +#[cfg(target_family = "windows")] +pub fn executable_extension() -> &'static str { + ".exe" +} diff --git a/src/tools/opt-dist/src/environment/windows.rs b/src/tools/opt-dist/src/environment/windows.rs index 90ddc01e893..f705461ac81 100644 --- a/src/tools/opt-dist/src/environment/windows.rs +++ b/src/tools/opt-dist/src/environment/windows.rs @@ -84,10 +84,6 @@ impl Environment for WindowsEnvironment { false } - fn executable_extension(&self) -> &'static str { - ".exe" - } - fn skipped_tests(&self) -> &'static [&'static str] { &[ // Fails as of June 2023. diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs index 3dd1a3223f5..e6b8df9c862 100644 --- a/src/tools/opt-dist/src/tests.rs +++ b/src/tools/opt-dist/src/tests.rs @@ -1,4 +1,4 @@ -use crate::environment::Environment; +use crate::environment::{executable_extension, Environment}; use crate::exec::cmd; use crate::utils::io::{copy_directory, find_file_in_dir, unpack_archive}; use anyhow::Context; @@ -45,9 +45,9 @@ pub fn run_tests(env: &dyn Environment) -> anyhow::Result<()> { &rustc_dir.join("lib").join("rustlib").join("src"), )?; - let rustc_path = rustc_dir.join("bin").join(format!("rustc{}", env.executable_extension())); + let rustc_path = rustc_dir.join("bin").join(format!("rustc{}", executable_extension())); assert!(rustc_path.is_file()); - let cargo_path = cargo_dir.join("bin").join(format!("cargo{}", env.executable_extension())); + let cargo_path = cargo_dir.join("bin").join(format!("cargo{}", executable_extension())); assert!(cargo_path.is_file()); // Specify path to a LLVM config so that LLVM is not rebuilt. @@ -56,7 +56,7 @@ pub fn run_tests(env: &dyn Environment) -> anyhow::Result<()> { .build_artifacts() .join("llvm") .join("bin") - .join(format!("llvm-config{}", env.executable_extension())); + .join(format!("llvm-config{}", executable_extension())); assert!(llvm_config.is_file()); let config_content = format!( diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs index 59c73fbd695..950dd1127a9 100644 --- a/src/tools/opt-dist/src/training.rs +++ b/src/tools/opt-dist/src/training.rs @@ -1,4 +1,4 @@ -use crate::environment::Environment; +use crate::environment::{executable_extension, Environment}; use crate::exec::{cmd, CmdBuilder}; use crate::utils::io::{count_files, delete_directory}; use crate::utils::with_log_group; @@ -86,7 +86,7 @@ fn merge_llvm_profiles( .build_artifacts() .join("llvm") .join("build") - .join(format!("bin/llvm-profdata{}", env.executable_extension())), + .join(format!("bin/llvm-profdata{}", executable_extension())), }; cmd(&[llvm_profdata.as_str(), "merge", "-o", merged_path.as_str(), profile_dir.as_str()]) From f17047bc9046b3e1218200c6b649ee59ac6d0a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 12 Sep 2023 18:32:32 +0200 Subject: [PATCH 139/250] Refactor Environment --- .github/workflows/ci.yml | 2 +- Cargo.lock | 76 +++++++- .../host-x86_64/dist-x86_64-linux/Dockerfile | 2 +- src/ci/github-actions/ci.yml | 2 +- src/tools/opt-dist/Cargo.toml | 2 + src/tools/opt-dist/src/environment.rs | 100 +++++++++++ src/tools/opt-dist/src/environment/linux.rs | 66 ------- src/tools/opt-dist/src/environment/mod.rs | 83 --------- src/tools/opt-dist/src/environment/windows.rs | 93 ---------- src/tools/opt-dist/src/exec.rs | 4 +- src/tools/opt-dist/src/main.rs | 162 ++++++++++++++++-- src/tools/opt-dist/src/tests.rs | 2 +- src/tools/opt-dist/src/training.rs | 16 +- src/tools/opt-dist/src/utils/mod.rs | 4 +- 14 files changed, 335 insertions(+), 279 deletions(-) create mode 100644 src/tools/opt-dist/src/environment.rs delete mode 100644 src/tools/opt-dist/src/environment/linux.rs delete mode 100644 src/tools/opt-dist/src/environment/mod.rs delete mode 100644 src/tools/opt-dist/src/environment/windows.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3680136d89f..0c20e4dc535 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -393,7 +393,7 @@ jobs: - name: dist-x86_64-msvc env: RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --host=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler" - SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist python x.py dist bootstrap --include-default-paths + SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist windows-ci -- python x.py dist bootstrap --include-default-paths DIST_REQUIRE_ALL_TOOLS: 1 os: windows-2019-8core-32gb - name: dist-i686-msvc diff --git a/Cargo.lock b/Cargo.lock index 295a5b43a6f..51fce77150c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -858,14 +858,38 @@ dependencies = [ "winapi", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + [[package]] name = "darling" version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.3", + "darling_macro 0.20.3", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", ] [[package]] @@ -882,13 +906,24 @@ dependencies = [ "syn 2.0.29", ] +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", + "quote", + "syn 1.0.109", +] + [[package]] name = "darling_macro" version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ - "darling_core", + "darling_core 0.20.3", "quote", "syn 2.0.29", ] @@ -919,6 +954,37 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_builder" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -938,7 +1004,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d" dependencies = [ - "darling", + "darling 0.20.3", "proc-macro2", "quote", "syn 2.0.29", @@ -2584,6 +2650,8 @@ dependencies = [ "anyhow", "build_helper", "camino", + "clap", + "derive_builder", "env_logger 0.10.0", "fs_extra", "glob", diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index f6954275ad4..6f1b2a6a638 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -87,7 +87,7 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.lto=thin ENV SCRIPT python3 ../x.py build --set rust.debug=true opt-dist && \ - ./build/$HOSTS/stage0-tools-bin/opt-dist python3 ../x.py dist \ + ./build/$HOSTS/stage0-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \ --host $HOSTS --target $HOSTS \ --include-default-paths \ build-manifest bootstrap diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 89b82d59d31..1712d65ece9 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -624,7 +624,7 @@ jobs: --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler - SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist python x.py dist bootstrap --include-default-paths + SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist windows-ci -- python x.py dist bootstrap --include-default-paths DIST_REQUIRE_ALL_TOOLS: 1 <<: *job-windows-8c diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml index 3f7dba81c3a..f1c3dd6aa9b 100644 --- a/src/tools/opt-dist/Cargo.toml +++ b/src/tools/opt-dist/Cargo.toml @@ -21,3 +21,5 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" glob = "0.3" tempfile = "3.5" +derive_builder = "0.12" +clap = { version = "4", features = ["derive"] } diff --git a/src/tools/opt-dist/src/environment.rs b/src/tools/opt-dist/src/environment.rs new file mode 100644 index 00000000000..e730e610604 --- /dev/null +++ b/src/tools/opt-dist/src/environment.rs @@ -0,0 +1,100 @@ +use camino::Utf8PathBuf; +use derive_builder::Builder; + +#[derive(Builder)] +pub struct Environment { + host_triple: String, + python_binary: String, + /// The rustc checkout, where the compiler source is located. + checkout_dir: Utf8PathBuf, + /// The main directory where the build occurs. + build_dir: Utf8PathBuf, + /// Directory where the optimization artifacts (PGO/BOLT profiles, etc.) + /// will be stored. + artifact_dir: Utf8PathBuf, + /// Path to the host LLVM used to compile LLVM in `src/llvm-project`. + host_llvm_dir: Utf8PathBuf, + /// List of test paths that should be skipped when testing the optimized artifacts. + skipped_tests: Vec<&'static str>, + use_bolt: bool, + shared_llvm: bool, +} + +impl Environment { + pub fn host_triple(&self) -> &str { + &self.host_triple + } + + pub fn python_binary(&self) -> &str { + &self.python_binary + } + + pub fn checkout_path(&self) -> Utf8PathBuf { + self.checkout_dir.clone() + } + + pub fn build_root(&self) -> Utf8PathBuf { + self.build_dir.clone() + } + + pub fn build_artifacts(&self) -> Utf8PathBuf { + self.build_root().join("build").join(&self.host_triple) + } + + pub fn artifact_dir(&self) -> Utf8PathBuf { + self.artifact_dir.clone() + } + + pub fn cargo_stage_0(&self) -> Utf8PathBuf { + self.build_artifacts() + .join("stage0") + .join("bin") + .join(format!("cargo{}", executable_extension())) + } + + pub fn rustc_stage_0(&self) -> Utf8PathBuf { + self.build_artifacts() + .join("stage0") + .join("bin") + .join(format!("rustc{}", executable_extension())) + } + + pub fn rustc_stage_2(&self) -> Utf8PathBuf { + self.build_artifacts() + .join("stage2") + .join("bin") + .join(format!("rustc{}", executable_extension())) + } + + /// Path to the built rustc-perf benchmark suite. + pub fn rustc_perf_dir(&self) -> Utf8PathBuf { + self.artifact_dir.join("rustc-perf") + } + + pub fn host_llvm_dir(&self) -> Utf8PathBuf { + self.host_llvm_dir.clone() + } + + pub fn use_bolt(&self) -> bool { + self.use_bolt + } + + pub fn supports_shared_llvm(&self) -> bool { + self.shared_llvm + } + + pub fn skipped_tests(&self) -> &[&'static str] { + &self.skipped_tests + } +} + +/// What is the extension of binary executables on this platform? +#[cfg(target_family = "unix")] +pub fn executable_extension() -> &'static str { + "" +} + +#[cfg(target_family = "windows")] +pub fn executable_extension() -> &'static str { + ".exe" +} diff --git a/src/tools/opt-dist/src/environment/linux.rs b/src/tools/opt-dist/src/environment/linux.rs deleted file mode 100644 index 61f05137298..00000000000 --- a/src/tools/opt-dist/src/environment/linux.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::environment::Environment; -use crate::exec::cmd; -use crate::utils::io::copy_directory; -use camino::{Utf8Path, Utf8PathBuf}; - -pub(super) struct LinuxEnvironment { - target_triple: String, -} - -impl LinuxEnvironment { - pub fn new(target_triple: String) -> Self { - Self { target_triple } - } -} - -impl Environment for LinuxEnvironment { - fn host_triple(&self) -> &str { - &self.target_triple - } - - fn python_binary(&self) -> &'static str { - "python3" - } - - fn checkout_path(&self) -> Utf8PathBuf { - Utf8PathBuf::from("/checkout") - } - - fn host_llvm_dir(&self) -> Utf8PathBuf { - Utf8PathBuf::from("/rustroot") - } - - fn opt_artifacts(&self) -> Utf8PathBuf { - Utf8PathBuf::from("/tmp/tmp-multistage/opt-artifacts") - } - - fn build_root(&self) -> Utf8PathBuf { - self.checkout_path().join("obj") - } - - fn prepare_rustc_perf(&self) -> anyhow::Result<()> { - // /tmp/rustc-perf comes from the x64 dist Dockerfile - copy_directory(Utf8Path::new("/tmp/rustc-perf"), &self.rustc_perf_dir())?; - cmd(&[self.cargo_stage_0().as_str(), "build", "-p", "collector"]) - .workdir(&self.rustc_perf_dir()) - .env("RUSTC", &self.rustc_stage_0().into_string()) - .env("RUSTC_BOOTSTRAP", "1") - .run()?; - Ok(()) - } - - fn supports_bolt(&self) -> bool { - true - } - - fn supports_shared_llvm(&self) -> bool { - true - } - - fn skipped_tests(&self) -> &'static [&'static str] { - &[ - // Fails because of linker errors, as of June 2023. - "tests/ui/process/nofile-limit.rs", - ] - } -} diff --git a/src/tools/opt-dist/src/environment/mod.rs b/src/tools/opt-dist/src/environment/mod.rs deleted file mode 100644 index 271b41316ab..00000000000 --- a/src/tools/opt-dist/src/environment/mod.rs +++ /dev/null @@ -1,83 +0,0 @@ -use camino::Utf8PathBuf; - -#[cfg(target_family = "unix")] -mod linux; -#[cfg(target_family = "windows")] -mod windows; - -pub trait Environment { - fn host_triple(&self) -> &str; - - fn python_binary(&self) -> &'static str; - - /// The rustc checkout, where the compiler source is located. - fn checkout_path(&self) -> Utf8PathBuf; - - /// Path to the host LLVM used to compile LLVM in `src/llvm-project`. - fn host_llvm_dir(&self) -> Utf8PathBuf; - - /// Directory where the optimization artifacts (PGO/BOLT profiles, etc.) - /// will be stored. - fn opt_artifacts(&self) -> Utf8PathBuf; - - /// The main directory where the build occurs. - fn build_root(&self) -> Utf8PathBuf; - - fn build_artifacts(&self) -> Utf8PathBuf { - self.build_root().join("build").join(self.host_triple()) - } - - fn cargo_stage_0(&self) -> Utf8PathBuf { - self.build_artifacts() - .join("stage0") - .join("bin") - .join(format!("cargo{}", executable_extension())) - } - - fn rustc_stage_0(&self) -> Utf8PathBuf { - self.build_artifacts() - .join("stage0") - .join("bin") - .join(format!("rustc{}", executable_extension())) - } - - fn rustc_stage_2(&self) -> Utf8PathBuf { - self.build_artifacts() - .join("stage2") - .join("bin") - .join(format!("rustc{}", executable_extension())) - } - - /// Path to the built rustc-perf benchmark suite. - fn rustc_perf_dir(&self) -> Utf8PathBuf { - self.opt_artifacts().join("rustc-perf") - } - - /// Download and/or compile rustc-perf. - fn prepare_rustc_perf(&self) -> anyhow::Result<()>; - - fn supports_bolt(&self) -> bool; - - fn supports_shared_llvm(&self) -> bool; - - /// List of test paths that should be skipped when testing the optimized artifacts. - fn skipped_tests(&self) -> &'static [&'static str]; -} - -pub fn create_environment(target_triple: String) -> Box { - #[cfg(target_family = "unix")] - return Box::new(linux::LinuxEnvironment::new(target_triple)); - #[cfg(target_family = "windows")] - return Box::new(windows::WindowsEnvironment::new(target_triple)); -} - -/// What is the extension of binary executables on this platform? -#[cfg(target_family = "unix")] -pub fn executable_extension() -> &'static str { - "" -} - -#[cfg(target_family = "windows")] -pub fn executable_extension() -> &'static str { - ".exe" -} diff --git a/src/tools/opt-dist/src/environment/windows.rs b/src/tools/opt-dist/src/environment/windows.rs deleted file mode 100644 index f705461ac81..00000000000 --- a/src/tools/opt-dist/src/environment/windows.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::environment::Environment; -use crate::exec::cmd; -use crate::utils::io::move_directory; -use crate::utils::retry_action; -use camino::Utf8PathBuf; -use std::io::Cursor; -use std::time::Duration; -use zip::ZipArchive; - -pub(super) struct WindowsEnvironment { - checkout_dir: Utf8PathBuf, - target_triple: String, -} - -impl WindowsEnvironment { - pub fn new(target_triple: String) -> Self { - Self { checkout_dir: std::env::current_dir().unwrap().try_into().unwrap(), target_triple } - } -} - -impl Environment for WindowsEnvironment { - fn host_triple(&self) -> &str { - &self.target_triple - } - - fn python_binary(&self) -> &'static str { - "python" - } - - fn checkout_path(&self) -> Utf8PathBuf { - self.checkout_dir.clone() - } - - fn host_llvm_dir(&self) -> Utf8PathBuf { - self.checkout_path().join("citools").join("clang-rust") - } - - fn opt_artifacts(&self) -> Utf8PathBuf { - self.checkout_path().join("opt-artifacts") - } - - fn build_root(&self) -> Utf8PathBuf { - self.checkout_path() - } - - fn prepare_rustc_perf(&self) -> anyhow::Result<()> { - // FIXME: add some mechanism for synchronization of this commit SHA with - // Linux (which builds rustc-perf in a Dockerfile) - // rustc-perf version from 2023-05-30 - const PERF_COMMIT: &str = "8b2ac3042e1ff2c0074455a0a3618adef97156b1"; - - let url = format!("https://ci-mirrors.rust-lang.org/rustc/rustc-perf-{PERF_COMMIT}.zip"); - let client = reqwest::blocking::Client::builder() - .timeout(Duration::from_secs(60 * 2)) - .connect_timeout(Duration::from_secs(60 * 2)) - .build()?; - let response = retry_action( - || Ok(client.get(&url).send()?.error_for_status()?.bytes()?.to_vec()), - "Download rustc-perf archive", - 5, - )?; - - let mut archive = ZipArchive::new(Cursor::new(response))?; - archive.extract(self.rustc_perf_dir())?; - move_directory( - &self.rustc_perf_dir().join(format!("rustc-perf-{PERF_COMMIT}")), - &self.rustc_perf_dir(), - )?; - - cmd(&[self.cargo_stage_0().as_str(), "build", "-p", "collector"]) - .workdir(&self.rustc_perf_dir()) - .env("RUSTC", &self.rustc_stage_0().into_string()) - .env("RUSTC_BOOTSTRAP", "1") - .run()?; - - Ok(()) - } - - fn supports_bolt(&self) -> bool { - false - } - - fn supports_shared_llvm(&self) -> bool { - false - } - - fn skipped_tests(&self) -> &'static [&'static str] { - &[ - // Fails as of June 2023. - "tests\\codegen\\vec-shrink-panik.rs", - ] - } -} diff --git a/src/tools/opt-dist/src/exec.rs b/src/tools/opt-dist/src/exec.rs index 4765dceb5dd..04e0184528a 100644 --- a/src/tools/opt-dist/src/exec.rs +++ b/src/tools/opt-dist/src/exec.rs @@ -96,7 +96,7 @@ pub struct Bootstrap { } impl Bootstrap { - pub fn build(env: &dyn Environment) -> Self { + pub fn build(env: &Environment) -> Self { let metrics_path = env.build_root().join("build").join("metrics.json"); let cmd = cmd(&[ env.python_binary(), @@ -114,7 +114,7 @@ impl Bootstrap { Self { cmd, metrics_path } } - pub fn dist(env: &dyn Environment, dist_args: &[String]) -> Self { + pub fn dist(env: &Environment, dist_args: &[String]) -> Self { let metrics_path = env.build_root().join("build").join("metrics.json"); let cmd = cmd(&dist_args.iter().map(|arg| arg.as_str()).collect::>()) .env("RUST_BACKTRACE", "full"); diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index 484ca5b3b86..6a3bdd7cc75 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -1,17 +1,22 @@ use crate::bolt::{bolt_optimize, with_bolt_instrumented}; use anyhow::Context; +use camino::{Utf8Path, Utf8PathBuf}; +use clap::Parser; use log::LevelFilter; +use std::io::Cursor; +use std::time::Duration; use utils::io; +use zip::ZipArchive; -use crate::environment::{create_environment, Environment}; -use crate::exec::Bootstrap; +use crate::environment::{Environment, EnvironmentBuilder}; +use crate::exec::{cmd, Bootstrap}; use crate::tests::run_tests; use crate::timer::Timer; use crate::training::{gather_llvm_bolt_profiles, gather_llvm_profiles, gather_rustc_profiles}; -use crate::utils::io::reset_directory; +use crate::utils::io::{copy_directory, move_directory, reset_directory}; use crate::utils::{ clear_llvm_files, format_env_variables, print_binary_sizes, print_free_disk_space, - with_log_group, + retry_action, with_log_group, }; mod bolt; @@ -23,24 +28,104 @@ mod timer; mod training; mod utils; +#[derive(clap::Parser, Debug)] +struct Args { + #[clap(subcommand)] + env: EnvironmentCmd, +} + +#[derive(clap::Parser, Clone, Debug)] +struct SharedArgs { + // Arguments passed to `x` to perform the final (dist) build. + build_args: Vec, +} + +#[derive(clap::Parser, Clone, Debug)] +enum EnvironmentCmd { + LinuxCi { + #[clap(flatten)] + shared: SharedArgs, + }, + WindowsCi { + #[clap(flatten)] + shared: SharedArgs, + }, +} + fn is_try_build() -> bool { std::env::var("DIST_TRY_BUILD").unwrap_or_else(|_| "0".to_string()) != "0" } +fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> { + let (env, args) = match args.env { + EnvironmentCmd::LinuxCi { shared } => { + let target_triple = + std::env::var("PGO_HOST").expect("PGO_HOST environment variable missing"); + + let checkout_dir = Utf8PathBuf::from("/checkout"); + let env = EnvironmentBuilder::default() + .host_triple(target_triple) + .python_binary("python3".to_string()) + .checkout_dir(checkout_dir.clone()) + .host_llvm_dir(Utf8PathBuf::from("/rustroot")) + .artifact_dir(Utf8PathBuf::from("/tmp/tmp-multistage/opt-artifacts")) + .build_dir(checkout_dir.join("obj")) + .shared_llvm(true) + .use_bolt(true) + .skipped_tests(vec![ + // Fails because of linker errors, as of June 2023. + "tests/ui/process/nofile-limit.rs", + ]) + .build()?; + // /tmp/rustc-perf comes from the x64 dist Dockerfile + with_log_group("Building rustc-perf", || { + Ok::<(), anyhow::Error>(copy_rustc_perf(&env, Utf8Path::new("/tmp/rustc-perf"))?) + })?; + + (env, shared.build_args) + } + EnvironmentCmd::WindowsCi { shared } => { + let target_triple = + std::env::var("PGO_HOST").expect("PGO_HOST environment variable missing"); + + let checkout_dir: Utf8PathBuf = std::env::current_dir()?.try_into()?; + let env = EnvironmentBuilder::default() + .host_triple(target_triple) + .python_binary("python".to_string()) + .checkout_dir(checkout_dir.clone()) + .host_llvm_dir(checkout_dir.join("citools").join("clang-rust")) + .artifact_dir(checkout_dir.join("opt-artifacts")) + .build_dir(checkout_dir) + .shared_llvm(false) + .use_bolt(false) + .skipped_tests(vec![ + // Fails as of June 2023. + "tests\\codegen\\vec-shrink-panik.rs", + ]) + .build()?; + + with_log_group("Building rustc-perf", || { + Ok::<(), anyhow::Error>(download_rustc_perf(&env)?) + })?; + + (env, shared.build_args) + } + }; + Ok((env, args)) +} + fn execute_pipeline( - env: &dyn Environment, + env: &Environment, timer: &mut Timer, dist_args: Vec, ) -> anyhow::Result<()> { - reset_directory(&env.opt_artifacts())?; - - with_log_group("Building rustc-perf", || env.prepare_rustc_perf())?; + reset_directory(&env.artifact_dir())?; // Stage 1: Build PGO instrumented rustc // We use a normal build of LLVM, because gathering PGO profiles for LLVM and `rustc` at the // same time can cause issues, because the host and in-tree LLVM versions can diverge. let rustc_pgo_profile = timer.section("Stage 1 (Rustc PGO)", |stage| { - let rustc_profile_dir_root = env.opt_artifacts().join("rustc-pgo"); + let rustc_profile_dir_root = env.artifact_dir().join("rustc-pgo"); stage.section("Build PGO instrumented rustc and LLVM", |section| { let mut builder = Bootstrap::build(env).rustc_pgo_instrument(&rustc_profile_dir_root); @@ -74,7 +159,7 @@ fn execute_pipeline( // Remove the previous, uninstrumented build of LLVM. clear_llvm_files(env)?; - let llvm_profile_dir_root = env.opt_artifacts().join("llvm-pgo"); + let llvm_profile_dir_root = env.artifact_dir().join("llvm-pgo"); stage.section("Build PGO instrumented LLVM", |section| { Bootstrap::build(env) @@ -95,7 +180,7 @@ fn execute_pipeline( Ok(profile) })?; - let llvm_bolt_profile = if env.supports_bolt() { + let llvm_bolt_profile = if env.use_bolt() { // Stage 3: Build BOLT instrumented LLVM // We build a PGO optimized LLVM in this step, then instrument it with BOLT and gather BOLT profiles. // Note that we don't remove LLVM artifacts after this step, so that they are reused in the final dist build. @@ -171,10 +256,9 @@ fn main() -> anyhow::Result<()> { .parse_default_env() .init(); - let target_triple = std::env::var("PGO_HOST").expect("PGO_HOST environment variable missing"); + let args = Args::parse(); - let mut build_args: Vec = std::env::args().skip(1).collect(); - println!("Running optimized build pipeline with args `{}`", build_args.join(" ")); + println!("Running optimized build pipeline with args `{:?}`", args); with_log_group("Environment values", || { println!("Environment values\n{}", format_env_variables()); @@ -186,6 +270,8 @@ fn main() -> anyhow::Result<()> { } }); + let (env, mut build_args) = create_environment(args).context("Cannot create environment")?; + // Skip components that are not needed for try builds to speed them up if is_try_build() { log::info!("Skipping building of unimportant components for a try build"); @@ -204,14 +290,56 @@ fn main() -> anyhow::Result<()> { } let mut timer = Timer::new(); - let env = create_environment(target_triple); - let result = execute_pipeline(env.as_ref(), &mut timer, build_args); + let result = execute_pipeline(&env, &mut timer, build_args); log::info!("Timer results\n{}", timer.format_stats()); print_free_disk_space()?; result.context("Optimized build pipeline has failed")?; - print_binary_sizes(env.as_ref())?; + print_binary_sizes(&env)?; Ok(()) } + +// Copy rustc-perf from the given path into the environment and build it. +fn copy_rustc_perf(env: &Environment, dir: &Utf8Path) -> anyhow::Result<()> { + copy_directory(dir, &env.rustc_perf_dir())?; + build_rustc_perf(env) +} + +// Download and build rustc-perf into the given environment. +fn download_rustc_perf(env: &Environment) -> anyhow::Result<()> { + // FIXME: add some mechanism for synchronization of this commit SHA with + // Linux (which builds rustc-perf in a Dockerfile) + // rustc-perf version from 2023-05-30 + const PERF_COMMIT: &str = "8b2ac3042e1ff2c0074455a0a3618adef97156b1"; + + let url = format!("https://ci-mirrors.rust-lang.org/rustc/rustc-perf-{PERF_COMMIT}.zip"); + let client = reqwest::blocking::Client::builder() + .timeout(Duration::from_secs(60 * 2)) + .connect_timeout(Duration::from_secs(60 * 2)) + .build()?; + let response = retry_action( + || Ok(client.get(&url).send()?.error_for_status()?.bytes()?.to_vec()), + "Download rustc-perf archive", + 5, + )?; + + let mut archive = ZipArchive::new(Cursor::new(response))?; + archive.extract(env.rustc_perf_dir())?; + move_directory( + &env.rustc_perf_dir().join(format!("rustc-perf-{PERF_COMMIT}")), + &env.rustc_perf_dir(), + )?; + + build_rustc_perf(env) +} + +fn build_rustc_perf(env: &Environment) -> anyhow::Result<()> { + cmd(&[env.cargo_stage_0().as_str(), "build", "-p", "collector"]) + .workdir(&env.rustc_perf_dir()) + .env("RUSTC", &env.rustc_stage_0().into_string()) + .env("RUSTC_BOOTSTRAP", "1") + .run()?; + Ok(()) +} diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs index e6b8df9c862..54f7185f88a 100644 --- a/src/tools/opt-dist/src/tests.rs +++ b/src/tools/opt-dist/src/tests.rs @@ -5,7 +5,7 @@ use anyhow::Context; use camino::{Utf8Path, Utf8PathBuf}; /// Run tests on optimized dist artifacts. -pub fn run_tests(env: &dyn Environment) -> anyhow::Result<()> { +pub fn run_tests(env: &Environment) -> anyhow::Result<()> { // After `dist` is executed, we extract its archived components into a sysroot directory, // and then use that extracted rustc as a stage0 compiler. // Then we run a subset of tests using that compiler, to have a basic smoke test which checks diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs index 950dd1127a9..dd8aa532408 100644 --- a/src/tools/opt-dist/src/training.rs +++ b/src/tools/opt-dist/src/training.rs @@ -30,7 +30,7 @@ const RUSTC_PGO_CRATES: &[&str] = &[ const LLVM_BOLT_CRATES: &[&str] = LLVM_PGO_CRATES; fn init_compiler_benchmarks( - env: &dyn Environment, + env: &Environment, profiles: &[&str], scenarios: &[&str], crates: &[&str], @@ -75,7 +75,7 @@ enum LlvmProfdata { } fn merge_llvm_profiles( - env: &dyn Environment, + env: &Environment, merged_path: &Utf8Path, profile_dir: &Utf8Path, profdata: LlvmProfdata, @@ -116,7 +116,7 @@ fn log_profile_stats( pub struct LlvmPGOProfile(pub Utf8PathBuf); pub fn gather_llvm_profiles( - env: &dyn Environment, + env: &Environment, profile_root: &Utf8Path, ) -> anyhow::Result { log::info!("Running benchmarks with PGO instrumented LLVM"); @@ -127,7 +127,7 @@ pub fn gather_llvm_profiles( .context("Cannot gather LLVM PGO profiles") })?; - let merged_profile = env.opt_artifacts().join("llvm-pgo.profdata"); + let merged_profile = env.artifact_dir().join("llvm-pgo.profdata"); log::info!("Merging LLVM PGO profiles to {merged_profile}"); merge_llvm_profiles(env, &merged_profile, profile_root, LlvmProfdata::Host)?; @@ -143,7 +143,7 @@ pub fn gather_llvm_profiles( pub struct RustcPGOProfile(pub Utf8PathBuf); pub fn gather_rustc_profiles( - env: &dyn Environment, + env: &Environment, profile_root: &Utf8Path, ) -> anyhow::Result { log::info!("Running benchmarks with PGO instrumented rustc"); @@ -163,7 +163,7 @@ pub fn gather_rustc_profiles( .context("Cannot gather rustc PGO profiles") })?; - let merged_profile = env.opt_artifacts().join("rustc-pgo.profdata"); + let merged_profile = env.artifact_dir().join("rustc-pgo.profdata"); log::info!("Merging Rustc PGO profiles to {merged_profile}"); merge_llvm_profiles(env, &merged_profile, profile_root, LlvmProfdata::Target)?; @@ -178,7 +178,7 @@ pub fn gather_rustc_profiles( pub struct LlvmBoltProfile(pub Utf8PathBuf); -pub fn gather_llvm_bolt_profiles(env: &dyn Environment) -> anyhow::Result { +pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result { log::info!("Running benchmarks with BOLT instrumented LLVM"); with_log_group("Running benchmarks", || { @@ -187,7 +187,7 @@ pub fn gather_llvm_bolt_profiles(env: &dyn Environment) -> anyhow::Result anyhow::Result<()> { Ok(()) } -pub fn print_binary_sizes(env: &dyn Environment) -> anyhow::Result<()> { +pub fn print_binary_sizes(env: &Environment) -> anyhow::Result<()> { use std::fmt::Write; let root = env.build_artifacts().join("stage2"); @@ -48,7 +48,7 @@ pub fn print_binary_sizes(env: &dyn Environment) -> anyhow::Result<()> { Ok(()) } -pub fn clear_llvm_files(env: &dyn Environment) -> anyhow::Result<()> { +pub fn clear_llvm_files(env: &Environment) -> anyhow::Result<()> { // Bootstrap currently doesn't support rebuilding LLVM when PGO options // change (or any other llvm-related options); so just clear out the relevant // directories ourselves. From a2ed508f547e5538ba6bd882ef76687aad06f4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 12 Sep 2023 19:32:46 +0200 Subject: [PATCH 140/250] Fix `reset_directory` function Before it was not deleting non-empty directories. --- src/tools/opt-dist/src/utils/io.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/opt-dist/src/utils/io.rs b/src/tools/opt-dist/src/utils/io.rs index 8bd516fa349..d24a1dc2d10 100644 --- a/src/tools/opt-dist/src/utils/io.rs +++ b/src/tools/opt-dist/src/utils/io.rs @@ -7,7 +7,7 @@ use std::path::Path; /// Delete and re-create the directory. pub fn reset_directory(path: &Utf8Path) -> anyhow::Result<()> { log::info!("Resetting directory {path}"); - let _ = std::fs::remove_dir(path); + let _ = std::fs::remove_dir_all(path); std::fs::create_dir_all(path)?; Ok(()) } From 11f9283da91ca4ab7fb634f3f586156f699007c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 12 Sep 2023 19:33:09 +0200 Subject: [PATCH 141/250] Add a Local environment to `opt-dist` This makes it easier to build a PGO/BOLT optimized `rustc` locally, outside of CI. --- src/tools/opt-dist/src/environment.rs | 7 +-- src/tools/opt-dist/src/main.rs | 75 ++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/tools/opt-dist/src/environment.rs b/src/tools/opt-dist/src/environment.rs index e730e610604..ed43f469c5e 100644 --- a/src/tools/opt-dist/src/environment.rs +++ b/src/tools/opt-dist/src/environment.rs @@ -7,7 +7,8 @@ pub struct Environment { python_binary: String, /// The rustc checkout, where the compiler source is located. checkout_dir: Utf8PathBuf, - /// The main directory where the build occurs. + /// The main directory where the build occurs. Stage0 rustc and cargo have to be available in + /// this directory before `opt-dist` is started. build_dir: Utf8PathBuf, /// Directory where the optimization artifacts (PGO/BOLT profiles, etc.) /// will be stored. @@ -15,7 +16,7 @@ pub struct Environment { /// Path to the host LLVM used to compile LLVM in `src/llvm-project`. host_llvm_dir: Utf8PathBuf, /// List of test paths that should be skipped when testing the optimized artifacts. - skipped_tests: Vec<&'static str>, + skipped_tests: Vec, use_bolt: bool, shared_llvm: bool, } @@ -83,7 +84,7 @@ impl Environment { self.shared_llvm } - pub fn skipped_tests(&self) -> &[&'static str] { + pub fn skipped_tests(&self) -> &[String] { &self.skipped_tests } } diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index 6a3bdd7cc75..b5e123360f8 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -42,10 +42,51 @@ struct SharedArgs { #[derive(clap::Parser, Clone, Debug)] enum EnvironmentCmd { + /// Perform a custom local PGO/BOLT optimized build. + Local { + /// Target triple of the host. + #[arg(long)] + target_triple: String, + + /// Checkout directory of `rustc`. + #[arg(long)] + checkout_dir: Utf8PathBuf, + + /// Host LLVM installation directory. + #[arg(long)] + llvm_dir: Utf8PathBuf, + + /// Python binary to use in bootstrap invocations. + #[arg(long, default_value = "python3")] + python: String, + + /// Directory where artifacts (like PGO profiles or rustc-perf) of this workflow + /// will be stored. + #[arg(long, default_value = "opt-artifacts")] + artifact_dir: Utf8PathBuf, + + /// Is LLVM for `rustc` built in shared library mode? + #[arg(long, default_value_t = true)] + llvm_shared: bool, + + /// Should BOLT optimization be used? If yes, host LLVM must have BOLT binaries + /// (`llvm-bolt` and `merge-fdata`) available. + #[arg(long, default_value_t = false)] + use_bolt: bool, + + /// Tests that should be skipped when testing the optimized compiler. + #[arg(long)] + skipped_tests: Vec, + + #[clap(flatten)] + shared: SharedArgs, + }, + /// Perform an optimized build on Linux CI, from inside Docker. LinuxCi { #[clap(flatten)] shared: SharedArgs, }, + /// Perform an optimized build on Windows CI, directly inside Github Actions. WindowsCi { #[clap(flatten)] shared: SharedArgs, @@ -58,6 +99,34 @@ fn is_try_build() -> bool { fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> { let (env, args) = match args.env { + EnvironmentCmd::Local { + target_triple, + checkout_dir, + llvm_dir, + python, + artifact_dir, + llvm_shared, + use_bolt, + skipped_tests, + shared, + } => { + let env = EnvironmentBuilder::default() + .host_triple(target_triple) + .python_binary(python) + .checkout_dir(checkout_dir.clone()) + .host_llvm_dir(llvm_dir) + .artifact_dir(artifact_dir) + .build_dir(checkout_dir) + .shared_llvm(llvm_shared) + .use_bolt(use_bolt) + .skipped_tests(skipped_tests) + .build()?; + with_log_group("Building rustc-perf", || { + Ok::<(), anyhow::Error>(download_rustc_perf(&env)?) + })?; + + (env, shared.build_args) + } EnvironmentCmd::LinuxCi { shared } => { let target_triple = std::env::var("PGO_HOST").expect("PGO_HOST environment variable missing"); @@ -74,7 +143,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> .use_bolt(true) .skipped_tests(vec![ // Fails because of linker errors, as of June 2023. - "tests/ui/process/nofile-limit.rs", + "tests/ui/process/nofile-limit.rs".to_string(), ]) .build()?; // /tmp/rustc-perf comes from the x64 dist Dockerfile @@ -100,7 +169,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> .use_bolt(false) .skipped_tests(vec![ // Fails as of June 2023. - "tests\\codegen\\vec-shrink-panik.rs", + "tests\\codegen\\vec-shrink-panik.rs".to_string(), ]) .build()?; @@ -309,6 +378,8 @@ fn copy_rustc_perf(env: &Environment, dir: &Utf8Path) -> anyhow::Result<()> { // Download and build rustc-perf into the given environment. fn download_rustc_perf(env: &Environment) -> anyhow::Result<()> { + reset_directory(&env.rustc_perf_dir())?; + // FIXME: add some mechanism for synchronization of this commit SHA with // Linux (which builds rustc-perf in a Dockerfile) // rustc-perf version from 2023-05-30 From 5b8a7a09178f73a581db2b21cf516fea7be9b49f Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 2 Jun 2023 13:55:46 +0200 Subject: [PATCH 142/250] `#[diagnostic::on_unimplemented]` without filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds support for a `#[diagnostic::on_unimplemented]` attribute with the following options: * `message` to customize the primary error message * `note` to add a customized note message to an error message * `label` to customize the label part of the error message Co-authored-by: León Orell Valerian Liehr Co-authored-by: Michael Goulet --- compiler/rustc_ast/src/attr/mod.rs | 16 +++ compiler/rustc_feature/src/active.rs | 2 +- compiler/rustc_lint_defs/src/builtin.rs | 11 +- compiler/rustc_middle/src/ty/mod.rs | 16 +++ compiler/rustc_passes/messages.ftl | 3 + compiler/rustc_passes/src/check_attr.rs | 22 +++- compiler/rustc_resolve/src/macros.rs | 5 +- compiler/rustc_trait_selection/messages.ftl | 2 + .../error_reporting/on_unimplemented.rs | 109 +++++++++++++---- .../feature-gate-diagnostic_namespace.rs | 4 +- .../feature-gate-diagnostic_namespace.stderr | 6 +- .../non_existing_attributes_accepted.stderr | 2 +- ...o_not_fail_parsing_on_invalid_options_1.rs | 37 ++++++ ...t_fail_parsing_on_invalid_options_1.stderr | 110 ++++++++++++++++++ ...eature-gate-diagnostic_on_unimplemented.rs | 7 ++ ...re-gate-diagnostic_on_unimplemented.stderr | 12 ++ .../on_unimplemented_simple.rs | 11 ++ .../on_unimplemented_simple.stderr | 24 ++++ 18 files changed, 360 insertions(+), 39 deletions(-) create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.rs create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.stderr create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.rs create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.stderr diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 19a2b3017bc..db008ea139d 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -99,6 +99,22 @@ impl Attribute { } } + pub fn path_matches(&self, name: &[Symbol]) -> bool { + match &self.kind { + AttrKind::Normal(normal) => { + normal.item.path.segments.len() == name.len() + && normal + .item + .path + .segments + .iter() + .zip(name) + .all(|(s, n)| s.args.is_none() && s.ident.name == *n) + } + AttrKind::DocComment(..) => false, + } + } + pub fn is_word(&self) -> bool { if let AttrKind::Normal(normal) = &self.kind { matches!(normal.item.args, AttrArgs::Empty) diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 1941390dc4a..e651fdc4ad2 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -411,7 +411,7 @@ declare_features! ( /// Allows having using `suggestion` in the `#[deprecated]` attribute. (active, deprecated_suggestion, "1.61.0", Some(94785), None), /// Allows using the `#[diagnostic]` attribute tool namespace - (active, diagnostic_namespace, "1.73.0", Some(94785), None), + (active, diagnostic_namespace, "1.73.0", Some(111996), None), /// Controls errors in trait implementations. (active, do_not_recommend, "1.67.0", Some(51992), None), /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 2567e273e11..63e147ec82f 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3404,8 +3404,8 @@ declare_lint_pass! { UNFULFILLED_LINT_EXPECTATIONS, UNINHABITED_STATIC, UNKNOWN_CRATE_TYPES, - UNKNOWN_DIAGNOSTIC_ATTRIBUTES, UNKNOWN_LINTS, + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNNAMEABLE_TEST_ITEMS, UNNAMEABLE_TYPES, UNREACHABLE_CODE, @@ -4419,7 +4419,8 @@ declare_lint! { } declare_lint! { - /// The `unknown_diagnostic_attributes` lint detects unrecognized diagnostic attributes. + /// The `unknown_or_malformed_diagnostic_attributes` lint detects unrecognized or otherwise malformed + /// diagnostic attributes. /// /// ### Example /// @@ -4431,15 +4432,17 @@ declare_lint! { /// /// {{produces}} /// + /// /// ### Explanation /// /// It is usually a mistake to specify a diagnostic attribute that does not exist. Check /// the spelling, and check the diagnostic attribute listing for the correct name. Also /// consider if you are using an old version of the compiler, and the attribute /// is only available in a newer version. - pub UNKNOWN_DIAGNOSTIC_ATTRIBUTES, + pub UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, Warn, - "unrecognized diagnostic attribute" + "unrecognized or malformed diagnostic attribute", + @feature_gate = sym::diagnostic_namespace; } declare_lint! { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 99b697d0ea5..ff420cf902a 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2408,6 +2408,22 @@ impl<'tcx> TyCtxt<'tcx> { } } + pub fn get_attrs_by_path<'attr>( + self, + did: DefId, + attr: &'attr [Symbol], + ) -> impl Iterator + 'attr + where + 'tcx: 'attr, + { + let filter_fn = move |a: &&ast::Attribute| a.path_matches(&attr); + if let Some(did) = did.as_local() { + self.hir().attrs(self.hir().local_def_id_to_hir_id(did)).iter().filter(filter_fn) + } else { + self.item_attrs(did).iter().filter(filter_fn) + } + } + pub fn get_attr(self, did: impl Into, attr: Symbol) -> Option<&'tcx ast::Attribute> { if cfg!(debug_assertions) && !rustc_feature::is_valid_for_get_attr(attr) { let did: DefId = did.into(); diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index eb1e1269f7f..a4abded67c5 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -140,6 +140,9 @@ passes_deprecated_annotation_has_no_effect = passes_deprecated_attribute = deprecated attribute must be paired with either stable or unstable attribute +passes_diagnostic_diagnostic_on_unimplemented_only_for_traits = + `#[diagnostic::on_unimplemented]` can only be applied to trait definitions + passes_diagnostic_item_first_defined = the diagnostic item is first defined here diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 50a087c7847..ba9e7ddaf51 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -16,6 +16,7 @@ use rustc_hir::{ self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID, }; use rustc_hir::{MethodKind, Target, Unsafety}; +use rustc_macros::LintDiagnostic; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::query::Providers; @@ -24,7 +25,7 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::builtin::{ CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, - UNUSED_ATTRIBUTES, + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Symbol}; @@ -36,6 +37,10 @@ use rustc_trait_selection::traits::ObligationCtxt; use std::cell::Cell; use std::collections::hash_map::Entry; +#[derive(LintDiagnostic)] +#[diag(passes_diagnostic_diagnostic_on_unimplemented_only_for_traits)] +pub struct DiagnosticOnUnimplementedOnlyForTraits; + pub(crate) fn target_from_impl_item<'tcx>( tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>, @@ -104,6 +109,9 @@ impl CheckAttrVisitor<'_> { let mut seen = FxHashMap::default(); let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { + if attr.path_matches(&[sym::diagnostic, sym::on_unimplemented]) { + self.check_diagnostic_on_unimplemented(attr.span, hir_id, target); + } match attr.name_or_empty() { sym::do_not_recommend => self.check_do_not_recommend(attr.span, target), sym::inline => self.check_inline(hir_id, attr, span, target), @@ -284,6 +292,18 @@ impl CheckAttrVisitor<'_> { } } + /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition + fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) { + if !matches!(target, Target::Trait) { + self.tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + DiagnosticOnUnimplementedOnlyForTraits, + ); + } + } + /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { match target { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 82060716575..90ae08ce37c 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -26,7 +26,7 @@ use rustc_middle::middle::stability; use rustc_middle::ty::RegisteredTools; use rustc_middle::ty::{TyCtxt, Visibility}; use rustc_session::lint::builtin::{ - LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNKNOWN_DIAGNOSTIC_ATTRIBUTES, + LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, }; use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES}; use rustc_session::lint::BuiltinLintDiagnostics; @@ -610,9 +610,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && path.segments.len() >= 2 && path.segments[0].ident.name == sym::diagnostic + && path.segments[1].ident.name != sym::on_unimplemented { self.tcx.sess.parse_sess.buffer_lint( - UNKNOWN_DIAGNOSTIC_ATTRIBUTES, + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, path.segments[1].span(), node_id, "unknown diagnostic attribute", diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 2db24c43734..20253b32add 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -27,6 +27,8 @@ trait_selection_inherent_projection_normalization_overflow = overflow evaluating trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]` .label = invalid on-clause here +trait_selection_malformed_on_unimplemented_attr = malformed `on_unimplemented` attribute + trait_selection_negative_positive_conflict = found both positive and negative implementation of trait `{$trait_desc}`{$self_desc -> [none] {""} *[default] {" "}for type `{$self_desc}` diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 0e73bad1918..e01713e3d88 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -9,6 +9,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; use rustc_parse_format::{ParseMode, Parser, Piece, Position}; +use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; use std::iter; @@ -336,6 +337,10 @@ pub enum AppendConstMessage { Custom(Symbol), } +#[derive(LintDiagnostic)] +#[diag(trait_selection_malformed_on_unimplemented_attr)] +pub struct NoValueInOnUnimplementedLint; + impl<'tcx> OnUnimplementedDirective { fn parse( tcx: TyCtxt<'tcx>, @@ -343,7 +348,8 @@ impl<'tcx> OnUnimplementedDirective { items: &[NestedMetaItem], span: Span, is_root: bool, - ) -> Result { + is_diagnostic_namespace_variant: bool, + ) -> Result, ErrorGuaranteed> { let mut errored = None; let mut item_iter = items.iter(); @@ -391,7 +397,10 @@ impl<'tcx> OnUnimplementedDirective { note = parse_value(note_)?; continue; } - } else if item.has_name(sym::parent_label) && parent_label.is_none() { + } else if item.has_name(sym::parent_label) + && parent_label.is_none() + && !is_diagnostic_namespace_variant + { if let Some(parent_label_) = item.value_str() { parent_label = parse_value(parent_label_)?; continue; @@ -401,15 +410,30 @@ impl<'tcx> OnUnimplementedDirective { && message.is_none() && label.is_none() && note.is_none() + && !is_diagnostic_namespace_variant + // FIXME(diagnostic_namespace): disallow filters for now { if let Some(items) = item.meta_item_list() { - match Self::parse(tcx, item_def_id, &items, item.span(), false) { - Ok(subcommand) => subcommands.push(subcommand), + match Self::parse( + tcx, + item_def_id, + &items, + item.span(), + false, + is_diagnostic_namespace_variant, + ) { + Ok(Some(subcommand)) => subcommands.push(subcommand), + Ok(None) => bug!( + "This cannot happen for now as we only reach that if `is_diagnostic_namespace_variant` is false" + ), Err(reported) => errored = Some(reported), }; continue; } - } else if item.has_name(sym::append_const_msg) && append_const_msg.is_none() { + } else if item.has_name(sym::append_const_msg) + && append_const_msg.is_none() + && !is_diagnostic_namespace_variant + { if let Some(msg) = item.value_str() { append_const_msg = Some(AppendConstMessage::Custom(msg)); continue; @@ -419,14 +443,23 @@ impl<'tcx> OnUnimplementedDirective { } } - // nothing found - tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() }); + if is_diagnostic_namespace_variant { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + vec![item.span()], + NoValueInOnUnimplementedLint, + ); + } else { + // nothing found + tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() }); + } } if let Some(reported) = errored { - Err(reported) + if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) } } else { - Ok(OnUnimplementedDirective { + Ok(Some(OnUnimplementedDirective { condition, subcommands, message, @@ -434,32 +467,58 @@ impl<'tcx> OnUnimplementedDirective { note, parent_label, append_const_msg, - }) + })) } } pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result, ErrorGuaranteed> { - let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) else { + let mut is_diagnostic_namespace_variant = false; + let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented).or_else(|| { + if tcx.features().diagnostic_namespace { + is_diagnostic_namespace_variant = true; + tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented]).next() + } else { + None + } + }) else { return Ok(None); }; let result = if let Some(items) = attr.meta_item_list() { - Self::parse(tcx, item_def_id, &items, attr.span, true).map(Some) + Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant) } else if let Some(value) = attr.value_str() { - Ok(Some(OnUnimplementedDirective { - condition: None, - message: None, - subcommands: vec![], - label: Some(OnUnimplementedFormatString::try_parse( - tcx, - item_def_id, - value, + if !is_diagnostic_namespace_variant { + Ok(Some(OnUnimplementedDirective { + condition: None, + message: None, + subcommands: vec![], + label: Some(OnUnimplementedFormatString::try_parse( + tcx, + item_def_id, + value, + attr.span, + )?), + note: None, + parent_label: None, + append_const_msg: None, + })) + } else { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), attr.span, - )?), - note: None, - parent_label: None, - append_const_msg: None, - })) + NoValueInOnUnimplementedLint, + ); + Ok(None) + } + } else if is_diagnostic_namespace_variant { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + attr.span, + NoValueInOnUnimplementedLint, + ); + Ok(None) } else { let reported = tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str"); diff --git a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs index a686ed9c84e..b08e291621f 100644 --- a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs +++ b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs @@ -1,12 +1,12 @@ #[diagnostic::non_existing_attribute] //~^ERROR `#[diagnostic]` attribute name space is experimental [E0658] -//~|WARNING unknown diagnostic attribute [unknown_diagnostic_attributes] +//~|WARNING unknown diagnostic attribute [unknown_or_malformed_diagnostic_attributes] pub trait Bar { } #[diagnostic::non_existing_attribute(with_option = "foo")] //~^ERROR `#[diagnostic]` attribute name space is experimental [E0658] -//~|WARNING unknown diagnostic attribute [unknown_diagnostic_attributes] +//~|WARNING unknown diagnostic attribute [unknown_or_malformed_diagnostic_attributes] struct Foo; fn main() { diff --git a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr index 45c95cbb3c7..017d00e2c8e 100644 --- a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr +++ b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr @@ -4,7 +4,7 @@ error[E0658]: `#[diagnostic]` attribute name space is experimental LL | #[diagnostic::non_existing_attribute] | ^^^^^^^^^^ | - = note: see issue #94785 for more information + = note: see issue #111996 for more information = help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable error[E0658]: `#[diagnostic]` attribute name space is experimental @@ -13,7 +13,7 @@ error[E0658]: `#[diagnostic]` attribute name space is experimental LL | #[diagnostic::non_existing_attribute(with_option = "foo")] | ^^^^^^^^^^ | - = note: see issue #94785 for more information + = note: see issue #111996 for more information = help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable warning: unknown diagnostic attribute @@ -22,7 +22,7 @@ warning: unknown diagnostic attribute LL | #[diagnostic::non_existing_attribute] | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: `#[warn(unknown_diagnostic_attributes)]` on by default + = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default warning: unknown diagnostic attribute --> $DIR/feature-gate-diagnostic_namespace.rs:7:15 diff --git a/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr b/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr index 4f9b7ba2bcf..753077b365e 100644 --- a/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr +++ b/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr @@ -4,7 +4,7 @@ warning: unknown diagnostic attribute LL | #[diagnostic::non_existing_attribute] | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: `#[warn(unknown_diagnostic_attributes)]` on by default + = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default warning: unknown diagnostic attribute --> $DIR/non_existing_attributes_accepted.rs:8:15 diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs new file mode 100644 index 00000000000..00fb59d14d7 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs @@ -0,0 +1,37 @@ +#![feature(diagnostic_namespace)] + +#[diagnostic::on_unimplemented(unsupported = "foo")] +//~^WARN malformed `on_unimplemented` attribute +//~|WARN malformed `on_unimplemented` attribute +trait Foo {} + +#[diagnostic::on_unimplemented(message = "Baz")] +//~^WARN `#[diagnostic::on_unimplemented]` can only be applied to trait definitions +struct Bar {} + +#[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] +//~^WARN malformed `on_unimplemented` attribute +//~|WARN malformed `on_unimplemented` attribute +trait Baz {} + +#[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] +//~^WARN malformed `on_unimplemented` attribute +//~|WARN malformed `on_unimplemented` attribute +trait Boom {} + +#[diagnostic::on_unimplemented = "boom"] +//~^WARN malformed `on_unimplemented` attribute +trait Doom {} + +fn take_foo(_: impl Foo) {} +fn take_baz(_: impl Baz) {} +fn take_boom(_: impl Boom) {} + +fn main() { + take_foo(1_i32); + //~^ERROR the trait bound `i32: Foo` is not satisfied + take_baz(1_i32); + //~^ERROR Boom + take_boom(1_i32); + //~^ERROR Boom +} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr new file mode 100644 index 00000000000..bb1b29ef248 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr @@ -0,0 +1,110 @@ +warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:8:1 + | +LL | #[diagnostic::on_unimplemented(message = "Baz")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32 + | +LL | #[diagnostic::on_unimplemented(unsupported = "foo")] + | ^^^^^^^^^^^^^^^^^^^ + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50 + | +LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] + | ^^^^^^^^^^^^^^^^^^^ + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50 + | +LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:22:1 + | +LL | #[diagnostic::on_unimplemented = "boom"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32 + | +LL | #[diagnostic::on_unimplemented(unsupported = "foo")] + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `i32: Foo` is not satisfied + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:14 + | +LL | take_foo(1_i32); + | -------- ^^^^^ the trait `Foo` is not implemented for `i32` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:6:1 + | +LL | trait Foo {} + | ^^^^^^^^^ +note: required by a bound in `take_foo` + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:26:21 + | +LL | fn take_foo(_: impl Foo) {} + | ^^^ required by this bound in `take_foo` + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50 + | +LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: Boom + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:14 + | +LL | take_baz(1_i32); + | -------- ^^^^^ the trait `Baz` is not implemented for `i32` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:15:1 + | +LL | trait Baz {} + | ^^^^^^^^^ +note: required by a bound in `take_baz` + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:27:21 + | +LL | fn take_baz(_: impl Baz) {} + | ^^^ required by this bound in `take_baz` + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50 + | +LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: Boom + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:35:15 + | +LL | take_boom(1_i32); + | --------- ^^^^^ the trait `Boom` is not implemented for `i32` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:20:1 + | +LL | trait Boom {} + | ^^^^^^^^^^ +note: required by a bound in `take_boom` + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:28:22 + | +LL | fn take_boom(_: impl Boom) {} + | ^^^^ required by this bound in `take_boom` + +error: aborting due to 3 previous errors; 8 warnings emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.rs b/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.rs new file mode 100644 index 00000000000..609a840c118 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.rs @@ -0,0 +1,7 @@ +#[diagnostic::on_unimplemented(message = "Foo")] +//~^ERROR `#[diagnostic]` attribute name space is experimental [E0658] +pub trait Bar { +} + +fn main() { +} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.stderr new file mode 100644 index 00000000000..21f02e3a73b --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.stderr @@ -0,0 +1,12 @@ +error[E0658]: `#[diagnostic]` attribute name space is experimental + --> $DIR/feature-gate-diagnostic_on_unimplemented.rs:1:3 + | +LL | #[diagnostic::on_unimplemented(message = "Foo")] + | ^^^^^^^^^^ + | + = note: see issue #111996 for more information + = help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.rs b/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.rs new file mode 100644 index 00000000000..797edbc9ec5 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.rs @@ -0,0 +1,11 @@ +#![feature(diagnostic_namespace)] + +#[diagnostic::on_unimplemented(message = "Foo", label = "Bar", note = "Baz")] +trait Foo {} + +fn takes_foo(_: impl Foo) {} + +fn main() { + takes_foo(()); + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.stderr new file mode 100644 index 00000000000..549c7caa720 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.stderr @@ -0,0 +1,24 @@ +error[E0277]: Foo + --> $DIR/on_unimplemented_simple.rs:9:15 + | +LL | takes_foo(()); + | --------- ^^ Bar + | | + | required by a bound introduced by this call + | + = help: the trait `Foo` is not implemented for `()` + = note: Baz +help: this trait has no implementations, consider adding one + --> $DIR/on_unimplemented_simple.rs:4:1 + | +LL | trait Foo {} + | ^^^^^^^^^ +note: required by a bound in `takes_foo` + --> $DIR/on_unimplemented_simple.rs:6:22 + | +LL | fn takes_foo(_: impl Foo) {} + | ^^^ required by this bound in `takes_foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From affe5a731525e5e8ce401248a2ead0e85cc5f7a9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 12 Sep 2023 23:10:03 +0200 Subject: [PATCH 143/250] fix: Temporarily skip decl check in derive expansions --- crates/hir-ty/src/diagnostics/decl_check.rs | 69 ++++++++++++++++----- crates/hir/src/lib.rs | 10 +-- crates/ide-diagnostics/src/lib.rs | 9 +-- 3 files changed, 63 insertions(+), 25 deletions(-) diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index a94a962c1f8..36d69edf9d5 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs @@ -163,25 +163,56 @@ impl<'a> DeclValidator<'a> { || allows.contains(allow::NONSTANDARD_STYLE) }) }; - - is_allowed(id) - // go upwards one step or give up - || match id { - AttrDefId::ModuleId(m) => m.containing_module(self.db.upcast()).map(|v| v.into()), - AttrDefId::FunctionId(f) => Some(f.lookup(self.db.upcast()).container.into()), - AttrDefId::StaticId(sid) => Some(sid.lookup(self.db.upcast()).container.into()), - AttrDefId::ConstId(cid) => Some(cid.lookup(self.db.upcast()).container.into()), - AttrDefId::TraitId(tid) => Some(tid.lookup(self.db.upcast()).container.into()), - AttrDefId::TraitAliasId(taid) => Some(taid.lookup(self.db.upcast()).container.into()), - AttrDefId::ImplId(iid) => Some(iid.lookup(self.db.upcast()).container.into()), - AttrDefId::ExternBlockId(id) => Some(id.lookup(self.db.upcast()).container.into()), - AttrDefId::ExternCrateId(id) => Some(id.lookup(self.db.upcast()).container.into()), - AttrDefId::UseId(id) => Some(id.lookup(self.db.upcast()).container.into()), + let db = self.db.upcast(); + let file_id_is_derive = || { + match id { + AttrDefId::ModuleId(m) => { + m.def_map(db)[m.local_id].origin.file_id().map(Into::into) + } + AttrDefId::FunctionId(f) => Some(f.lookup(db).id.file_id()), + AttrDefId::StaticId(sid) => Some(sid.lookup(db).id.file_id()), + AttrDefId::ConstId(cid) => Some(cid.lookup(db).id.file_id()), + AttrDefId::TraitId(tid) => Some(tid.lookup(db).id.file_id()), + AttrDefId::TraitAliasId(taid) => Some(taid.lookup(db).id.file_id()), + AttrDefId::ImplId(iid) => Some(iid.lookup(db).id.file_id()), + AttrDefId::ExternBlockId(id) => Some(id.lookup(db).id.file_id()), + AttrDefId::ExternCrateId(id) => Some(id.lookup(db).id.file_id()), + AttrDefId::UseId(id) => Some(id.lookup(db).id.file_id()), // These warnings should not explore macro definitions at all AttrDefId::MacroId(_) => None, AttrDefId::AdtId(aid) => match aid { - AdtId::StructId(sid) => Some(sid.lookup(self.db.upcast()).container.into()), - AdtId::EnumId(eid) => Some(eid.lookup(self.db.upcast()).container.into()), + AdtId::StructId(sid) => Some(sid.lookup(db).id.file_id()), + AdtId::EnumId(eid) => Some(eid.lookup(db).id.file_id()), + // Unions aren't yet supported + AdtId::UnionId(_) => None, + }, + AttrDefId::FieldId(_) => None, + AttrDefId::EnumVariantId(_) => None, + AttrDefId::TypeAliasId(_) => None, + AttrDefId::GenericParamId(_) => None, + } + .map_or(false, |file_id| { + file_id.is_custom_derive(db.upcast()) || file_id.is_builtin_derive(db.upcast()) + }) + }; + + let parent = || { + match id { + AttrDefId::ModuleId(m) => m.containing_module(db).map(|v| v.into()), + AttrDefId::FunctionId(f) => Some(f.lookup(db).container.into()), + AttrDefId::StaticId(sid) => Some(sid.lookup(db).container.into()), + AttrDefId::ConstId(cid) => Some(cid.lookup(db).container.into()), + AttrDefId::TraitId(tid) => Some(tid.lookup(db).container.into()), + AttrDefId::TraitAliasId(taid) => Some(taid.lookup(db).container.into()), + AttrDefId::ImplId(iid) => Some(iid.lookup(db).container.into()), + AttrDefId::ExternBlockId(id) => Some(id.lookup(db).container.into()), + AttrDefId::ExternCrateId(id) => Some(id.lookup(db).container.into()), + AttrDefId::UseId(id) => Some(id.lookup(db).container.into()), + // These warnings should not explore macro definitions at all + AttrDefId::MacroId(_) => None, + AttrDefId::AdtId(aid) => match aid { + AdtId::StructId(sid) => Some(sid.lookup(db).container.into()), + AdtId::EnumId(eid) => Some(eid.lookup(db).container.into()), // Unions aren't yet supported AdtId::UnionId(_) => None, }, @@ -191,6 +222,12 @@ impl<'a> DeclValidator<'a> { AttrDefId::GenericParamId(_) => None, } .is_some_and(|mid| self.allowed(mid, allow_name, true)) + }; + is_allowed(id) + // FIXME: this is a hack to avoid false positives in derive macros currently + || file_id_is_derive() + // go upwards one step or give up + || parent() } fn validate_func(&mut self, func: FunctionId) { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 453f9b93737..b215ed38f28 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -564,8 +564,8 @@ impl Module { emit_def_diagnostic(db, acc, diag); } - for decl in self.declarations(db) { - match decl { + for def in self.declarations(db) { + match def { ModuleDef::Module(m) => { // Only add diagnostics from inline modules if def_map[m.id.local_id].origin.is_inline() { @@ -576,7 +576,7 @@ impl Module { for diag in db.trait_data_with_diagnostics(t.id).1.iter() { emit_def_diagnostic(db, acc, diag); } - acc.extend(decl.diagnostics(db)) + acc.extend(def.diagnostics(db)) } ModuleDef::Adt(adt) => { match adt { @@ -600,10 +600,10 @@ impl Module { } } } - acc.extend(decl.diagnostics(db)) + acc.extend(def.diagnostics(db)) } ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m), - _ => acc.extend(decl.diagnostics(db)), + _ => acc.extend(def.diagnostics(db)), } } self.legacy_macros(db).into_iter().for_each(|m| emit_macro_def_diagnostics(db, acc, m)); diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 16ddb2aaf38..ebe197a6790 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -433,7 +433,8 @@ fn handle_lint_attributes( diagnostics_of_range: &mut FxHashMap, &mut Diagnostic>, ) { let file_id = sema.hir_file_for(root); - for ev in root.preorder() { + let mut preorder = root.preorder(); + while let Some(ev) = preorder.next() { match ev { syntax::WalkEvent::Enter(node) => { for attr in node.children().filter_map(ast::Attr::cast) { @@ -516,7 +517,7 @@ fn parse_lint_attribute( let Some((tag, args_tt)) = attr.as_simple_call() else { return; }; - let serevity = match tag.as_str() { + let severity = match tag.as_str() { "allow" => Severity::Allow, "warn" => Severity::Warning, "forbid" | "deny" => Severity::Error, @@ -524,12 +525,12 @@ fn parse_lint_attribute( }; for lint in parse_tt_as_comma_sep_paths(args_tt).into_iter().flatten() { if let Some(lint) = lint.as_single_name_ref() { - job(rustc_stack.entry(lint.to_string()).or_default(), serevity); + job(rustc_stack.entry(lint.to_string()).or_default(), severity); } if let Some(tool) = lint.qualifier().and_then(|x| x.as_single_name_ref()) { if let Some(name_ref) = &lint.segment().and_then(|x| x.name_ref()) { if tool.to_string() == "clippy" { - job(clippy_stack.entry(name_ref.to_string()).or_default(), serevity); + job(clippy_stack.entry(name_ref.to_string()).or_default(), severity); } } } From 2962528b4accdf1d0c2b219568c4ef115f78a503 Mon Sep 17 00:00:00 2001 From: Kai Luo Date: Wed, 13 Sep 2023 15:04:34 +0800 Subject: [PATCH 144/250] Make AIX known by bootstrap --- src/bootstrap/bootstrap.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index e4c76226454..bc8b5326d8d 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -312,6 +312,8 @@ def default_build_triple(verbose): # non-standard string (e.g. gnuwin32 tools returns `windows32`). In # these cases, fall back to using sys.platform. return 'x86_64-pc-windows-msvc' + elif kernel == 'AIX': + return 'powerpc64-ibm-aix' else: err = "unknown OS type: {}".format(kernel) sys.exit(err) From fc17e09029a4f01fb59778e8575faa99a08f289a Mon Sep 17 00:00:00 2001 From: Kai Luo Date: Wed, 13 Sep 2023 16:05:50 +0800 Subject: [PATCH 145/250] Add comment to elaborate --- src/bootstrap/bootstrap.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index bc8b5326d8d..eca3a522f80 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -313,6 +313,12 @@ def default_build_triple(verbose): # these cases, fall back to using sys.platform. return 'x86_64-pc-windows-msvc' elif kernel == 'AIX': + # `uname -m` returns the machine ID rather than machine hardware on AIX, + # so we are unable to use cputype to form triple. Since AIX 7.2 and + # above supports 32-bit and 64-bit mode simultaneously and `uname -p` + # returns `powerpc`. Currently we only supports `powerpc64-ibm-aix` in + # rust. For above reasons, kerneltype_mapper and cputype_mapper are not + # used to infer AIX's triple. return 'powerpc64-ibm-aix' else: err = "unknown OS type: {}".format(kernel) From 5049a7167ca51be7f9bfbcf2f75eb9061722860b Mon Sep 17 00:00:00 2001 From: Kai Luo Date: Wed, 13 Sep 2023 16:08:23 +0800 Subject: [PATCH 146/250] Adjust comment --- src/bootstrap/bootstrap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index eca3a522f80..6e0326a7d10 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -317,8 +317,8 @@ def default_build_triple(verbose): # so we are unable to use cputype to form triple. Since AIX 7.2 and # above supports 32-bit and 64-bit mode simultaneously and `uname -p` # returns `powerpc`. Currently we only supports `powerpc64-ibm-aix` in - # rust. For above reasons, kerneltype_mapper and cputype_mapper are not - # used to infer AIX's triple. + # rust on AIX. For above reasons, kerneltype_mapper and cputype_mapper + # are not used to infer AIX's triple. return 'powerpc64-ibm-aix' else: err = "unknown OS type: {}".format(kernel) From 122d1cc8c332b4dd176e1475a063688e73d95168 Mon Sep 17 00:00:00 2001 From: Kai Luo Date: Wed, 13 Sep 2023 16:10:26 +0800 Subject: [PATCH 147/250] Adjust comment --- src/bootstrap/bootstrap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 6e0326a7d10..a9aa7524e8b 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -314,9 +314,9 @@ def default_build_triple(verbose): return 'x86_64-pc-windows-msvc' elif kernel == 'AIX': # `uname -m` returns the machine ID rather than machine hardware on AIX, - # so we are unable to use cputype to form triple. Since AIX 7.2 and + # so we are unable to use cputype to form triple. AIX 7.2 and # above supports 32-bit and 64-bit mode simultaneously and `uname -p` - # returns `powerpc`. Currently we only supports `powerpc64-ibm-aix` in + # returns `powerpc`, however we only supports `powerpc64-ibm-aix` in # rust on AIX. For above reasons, kerneltype_mapper and cputype_mapper # are not used to infer AIX's triple. return 'powerpc64-ibm-aix' From 6c718b5b8a98ba756c69e8a2019131c6a514e64d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 13 Sep 2023 18:11:34 +0200 Subject: [PATCH 148/250] Refactor rustc-perf building --- src/tools/opt-dist/src/environment.rs | 6 ++++++ src/tools/opt-dist/src/main.rs | 18 +++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/tools/opt-dist/src/environment.rs b/src/tools/opt-dist/src/environment.rs index ed43f469c5e..f7b5c176375 100644 --- a/src/tools/opt-dist/src/environment.rs +++ b/src/tools/opt-dist/src/environment.rs @@ -17,6 +17,8 @@ pub struct Environment { host_llvm_dir: Utf8PathBuf, /// List of test paths that should be skipped when testing the optimized artifacts. skipped_tests: Vec, + /// Directory containing a pre-built rustc-perf checkout. + prebuilt_rustc_perf: Option, use_bolt: bool, shared_llvm: bool, } @@ -67,6 +69,10 @@ impl Environment { .join(format!("rustc{}", executable_extension())) } + pub fn prebuilt_rustc_perf(&self) -> Option { + self.prebuilt_rustc_perf.clone() + } + /// Path to the built rustc-perf benchmark suite. pub fn rustc_perf_dir(&self) -> Utf8PathBuf { self.artifact_dir.join("rustc-perf") diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index b5e123360f8..978e2dfa4e8 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -121,9 +121,6 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> .use_bolt(use_bolt) .skipped_tests(skipped_tests) .build()?; - with_log_group("Building rustc-perf", || { - Ok::<(), anyhow::Error>(download_rustc_perf(&env)?) - })?; (env, shared.build_args) } @@ -139,6 +136,8 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> .host_llvm_dir(Utf8PathBuf::from("/rustroot")) .artifact_dir(Utf8PathBuf::from("/tmp/tmp-multistage/opt-artifacts")) .build_dir(checkout_dir.join("obj")) + // /tmp/rustc-perf comes from the x64 dist Dockerfile + .prebuilt_rustc_perf(Some(Utf8PathBuf::from("/tmp/rustc-perf"))) .shared_llvm(true) .use_bolt(true) .skipped_tests(vec![ @@ -146,10 +145,6 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> "tests/ui/process/nofile-limit.rs".to_string(), ]) .build()?; - // /tmp/rustc-perf comes from the x64 dist Dockerfile - with_log_group("Building rustc-perf", || { - Ok::<(), anyhow::Error>(copy_rustc_perf(&env, Utf8Path::new("/tmp/rustc-perf"))?) - })?; (env, shared.build_args) } @@ -173,10 +168,6 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> ]) .build()?; - with_log_group("Building rustc-perf", || { - Ok::<(), anyhow::Error>(download_rustc_perf(&env)?) - })?; - (env, shared.build_args) } }; @@ -190,6 +181,11 @@ fn execute_pipeline( ) -> anyhow::Result<()> { reset_directory(&env.artifact_dir())?; + with_log_group("Building rustc-perf", || match env.prebuilt_rustc_perf() { + Some(dir) => copy_rustc_perf(env, &dir), + None => download_rustc_perf(env), + })?; + // Stage 1: Build PGO instrumented rustc // We use a normal build of LLVM, because gathering PGO profiles for LLVM and `rustc` at the // same time can cause issues, because the host and in-tree LLVM versions can diverge. From f13b54546bb7aea9beee7d3f9178882a7f10f45c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 13 Sep 2023 18:12:41 +0200 Subject: [PATCH 149/250] Resolve clippy warnings --- src/tools/opt-dist/src/tests.rs | 6 +++--- src/tools/opt-dist/src/training.rs | 2 +- src/tools/opt-dist/src/utils/io.rs | 6 ++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs index 54f7185f88a..31aabca09f3 100644 --- a/src/tools/opt-dist/src/tests.rs +++ b/src/tools/opt-dist/src/tests.rs @@ -33,8 +33,8 @@ pub fn run_tests(env: &Environment) -> anyhow::Result<()> { // We need to manually copy libstd to the extracted rustc sysroot copy_directory( - &libstd_dir.join("lib").join("rustlib").join(&host_triple).join("lib"), - &rustc_dir.join("lib").join("rustlib").join(&host_triple).join("lib"), + &libstd_dir.join("lib").join("rustlib").join(host_triple).join("lib"), + &rustc_dir.join("lib").join("rustlib").join(host_triple).join("lib"), )?; // Extract sources - they aren't in the `rustc-nightly-{host}` tarball, so we need to manually copy libstd @@ -109,6 +109,6 @@ fn find_dist_version(directory: &Utf8Path) -> anyhow::Result { .unwrap() .to_string(); let (version, _) = - archive.strip_prefix("reproducible-artifacts-").unwrap().split_once("-").unwrap(); + archive.strip_prefix("reproducible-artifacts-").unwrap().split_once('-').unwrap(); Ok(version.to_string()) } diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs index dd8aa532408..274f4cea0ab 100644 --- a/src/tools/opt-dist/src/training.rs +++ b/src/tools/opt-dist/src/training.rs @@ -192,7 +192,7 @@ pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result = - glob::glob(&format!("{profile_root}*"))?.into_iter().collect::, _>>()?; + glob::glob(&format!("{profile_root}*"))?.collect::, _>>()?; let mut merge_args = vec!["merge-fdata"]; merge_args.extend(profiles.iter().map(|p| p.to_str().unwrap())); diff --git a/src/tools/opt-dist/src/utils/io.rs b/src/tools/opt-dist/src/utils/io.rs index d24a1dc2d10..d6bd5cb85e4 100644 --- a/src/tools/opt-dist/src/utils/io.rs +++ b/src/tools/opt-dist/src/utils/io.rs @@ -63,7 +63,6 @@ pub fn get_files_from_dir( let path = format!("{dir}/*{}", suffix.unwrap_or("")); Ok(glob::glob(&path)? - .into_iter() .map(|p| p.map(|p| Utf8PathBuf::from_path_buf(p).unwrap())) .collect::, _>>()?) } @@ -74,9 +73,8 @@ pub fn find_file_in_dir( prefix: &str, suffix: &str, ) -> anyhow::Result { - let files = glob::glob(&format!("{directory}/{prefix}*{suffix}"))? - .into_iter() - .collect::, _>>()?; + let files = + glob::glob(&format!("{directory}/{prefix}*{suffix}"))?.collect::, _>>()?; match files.len() { 0 => Err(anyhow::anyhow!("No file with prefix {prefix} found in {directory}")), 1 => Ok(Utf8PathBuf::from_path_buf(files[0].clone()).unwrap()), From 712e67cf11fbf76c4be77e221f030a39fbe4f195 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 13 Sep 2023 22:01:04 +0200 Subject: [PATCH 150/250] fix: Fix lens location "above_whole_item" breaking lenses --- crates/ide-db/src/search.rs | 4 +++- crates/ide/src/annotations.rs | 3 +-- crates/rust-analyzer/src/lsp/to_proto.rs | 27 +++++++++++------------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 2ffce3c7513..9c4f0ac8c9f 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -6,7 +6,7 @@ use std::mem; -use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; +use base_db::{salsa::Database, FileId, FileRange, SourceDatabase, SourceDatabaseExt}; use hir::{ AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility, }; @@ -470,6 +470,7 @@ impl<'a> FindUsages<'a> { }; for (text, file_id, search_range) in scope_files(sema, &search_scope) { + self.sema.db.unwind_if_cancelled(); let tree = Lazy::new(move || sema.parse(file_id).syntax().clone()); // Search for occurrences of the items name @@ -506,6 +507,7 @@ impl<'a> FindUsages<'a> { let finder = &Finder::new("super"); for (text, file_id, search_range) in scope_files(sema, &scope) { + self.sema.db.unwind_if_cancelled(); let tree = Lazy::new(move || sema.parse(file_id).syntax().clone()); for offset in match_indices(&text, finder, search_range) { diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index f994c284c71..fb79b5dc211 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -94,10 +94,9 @@ pub(crate) fn annotations( enum_ .variants(db) .into_iter() - .map(|variant| { + .filter_map(|variant| { variant.source(db).and_then(|node| name_range(db, node, file_id)) }) - .flatten() .for_each(|range| { let (annotation_range, target_position) = mk_ranges(range); annotations.push(Annotation { diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index ce2f993ca15..23074493aee 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -1358,17 +1358,18 @@ pub(crate) fn code_lens( }) } } - AnnotationKind::HasImpls { pos: file_range, data } => { + AnnotationKind::HasImpls { pos, data } => { if !client_commands_config.show_reference { return Ok(()); } - let line_index = snap.file_line_index(file_range.file_id)?; + let line_index = snap.file_line_index(pos.file_id)?; let annotation_range = range(&line_index, annotation.range); - let url = url(snap, file_range.file_id); + let url = url(snap, pos.file_id); + let pos = position(&line_index, pos.offset); let id = lsp_types::TextDocumentIdentifier { uri: url.clone() }; - let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start); + let doc_pos = lsp_types::TextDocumentPositionParams::new(id, pos); let goto_params = lsp_types::request::GotoImplementationParams { text_document_position_params: doc_pos, @@ -1391,7 +1392,7 @@ pub(crate) fn code_lens( command::show_references( implementation_title(locations.len()), &url, - annotation_range.start, + pos, locations, ) }); @@ -1411,28 +1412,24 @@ pub(crate) fn code_lens( })(), }) } - AnnotationKind::HasReferences { pos: file_range, data } => { + AnnotationKind::HasReferences { pos, data } => { if !client_commands_config.show_reference { return Ok(()); } - let line_index = snap.file_line_index(file_range.file_id)?; + let line_index = snap.file_line_index(pos.file_id)?; let annotation_range = range(&line_index, annotation.range); - let url = url(snap, file_range.file_id); + let url = url(snap, pos.file_id); + let pos = position(&line_index, pos.offset); let id = lsp_types::TextDocumentIdentifier { uri: url.clone() }; - let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start); + let doc_pos = lsp_types::TextDocumentPositionParams::new(id, pos); let command = data.map(|ranges| { let locations: Vec = ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect(); - command::show_references( - reference_title(locations.len()), - &url, - annotation_range.start, - locations, - ) + command::show_references(reference_title(locations.len()), &url, pos, locations) }); acc.push(lsp_types::CodeLens { From a219dbda2b05d059061c4ea4321b87e0feeb225c Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 13 Sep 2023 22:02:28 +0000 Subject: [PATCH 151/250] Remove most of the duplication from `Semantics{,Impl}` via deref --- crates/hir/src/semantics.rs | 446 ++++++++---------------------------- 1 file changed, 97 insertions(+), 349 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index b8d4ecd4414..a42e0978b25 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -127,165 +127,24 @@ impl fmt::Debug for Semantics<'_, DB> { } } +impl<'db, DB> ops::Deref for Semantics<'db, DB> { + type Target = SemanticsImpl<'db>; + + fn deref(&self) -> &Self::Target { + &self.imp + } +} + impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn new(db: &DB) -> Semantics<'_, DB> { let impl_ = SemanticsImpl::new(db); Semantics { db, imp: impl_ } } - pub fn parse(&self, file_id: FileId) -> ast::SourceFile { - self.imp.parse(file_id) - } - - pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode { - self.imp.parse_or_expand(file_id) - } - - pub fn expand(&self, macro_call: &ast::MacroCall) -> Option { - self.imp.expand(macro_call) - } - - /// If `item` has an attribute macro attached to it, expands it. - pub fn expand_attr_macro(&self, item: &ast::Item) -> Option { - self.imp.expand_attr_macro(item) - } - - pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option { - self.imp.expand_derive_as_pseudo_attr_macro(attr) - } - - pub fn resolve_derive_macro(&self, derive: &ast::Attr) -> Option>> { - self.imp.resolve_derive_macro(derive) - } - - pub fn expand_derive_macro(&self, derive: &ast::Attr) -> Option> { - self.imp.expand_derive_macro(derive) - } - - pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool { - self.imp.is_attr_macro_call(item) - } - - pub fn is_derive_annotated(&self, item: &ast::Adt) -> bool { - self.imp.is_derive_annotated(item) - } - - /// Expand the macro call with a different token tree, mapping the `token_to_map` down into the - /// expansion. `token_to_map` should be a token from the `speculative args` node. - pub fn speculative_expand( - &self, - actual_macro_call: &ast::MacroCall, - speculative_args: &ast::TokenTree, - token_to_map: SyntaxToken, - ) -> Option<(SyntaxNode, SyntaxToken)> { - self.imp.speculative_expand(actual_macro_call, speculative_args, token_to_map) - } - - /// Expand the macro call with a different item as the input, mapping the `token_to_map` down into the - /// expansion. `token_to_map` should be a token from the `speculative args` node. - pub fn speculative_expand_attr_macro( - &self, - actual_macro_call: &ast::Item, - speculative_args: &ast::Item, - token_to_map: SyntaxToken, - ) -> Option<(SyntaxNode, SyntaxToken)> { - self.imp.speculative_expand_attr(actual_macro_call, speculative_args, token_to_map) - } - - pub fn speculative_expand_derive_as_pseudo_attr_macro( - &self, - actual_macro_call: &ast::Attr, - speculative_args: &ast::Attr, - token_to_map: SyntaxToken, - ) -> Option<(SyntaxNode, SyntaxToken)> { - self.imp.speculative_expand_derive_as_pseudo_attr_macro( - actual_macro_call, - speculative_args, - token_to_map, - ) - } - - /// Descend the token into its macro call if it is part of one, returning the token in the - /// expansion that it is associated with. If `offset` points into the token's range, it will - /// be considered for the mapping in case of inline format args. - pub fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken { - self.imp.descend_into_macros_single(token, offset) - } - - /// Descend the token into its macro call if it is part of one, returning the tokens in the - /// expansion that it is associated with. If `offset` points into the token's range, it will - /// be considered for the mapping in case of inline format args. - pub fn descend_into_macros( - &self, - token: SyntaxToken, - offset: TextSize, - ) -> SmallVec<[SyntaxToken; 1]> { - self.imp.descend_into_macros(token, offset) - } - - /// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token. - /// - /// Returns the original non descended token if none of the mapped counterparts have the same text. - pub fn descend_into_macros_with_same_text( - &self, - token: SyntaxToken, - offset: TextSize, - ) -> SmallVec<[SyntaxToken; 1]> { - self.imp.descend_into_macros_with_same_text(token, offset) - } - - pub fn descend_into_macros_with_kind_preference( - &self, - token: SyntaxToken, - offset: TextSize, - ) -> SyntaxToken { - self.imp.descend_into_macros_with_kind_preference(token, offset) - } - - /// Maps a node down by mapping its first and last token down. - pub fn descend_node_into_attributes(&self, node: N) -> SmallVec<[N; 1]> { - self.imp.descend_node_into_attributes(node) - } - - /// Search for a definition's source and cache its syntax tree - pub fn source(&self, def: Def) -> Option> - where - Def::Ast: AstNode, - { - self.imp.source(def) - } - pub fn hir_file_for(&self, syntax_node: &SyntaxNode) -> HirFileId { self.imp.find_file(syntax_node).file_id } - /// Attempts to map the node out of macro expanded files returning the original file range. - /// If upmapping is not possible, this will fall back to the range of the macro call of the - /// macro file the node resides in. - pub fn original_range(&self, node: &SyntaxNode) -> FileRange { - self.imp.original_range(node) - } - - /// Attempts to map the node out of macro expanded files returning the original file range. - pub fn original_range_opt(&self, node: &SyntaxNode) -> Option { - self.imp.original_range_opt(node) - } - - /// Attempts to map the node out of macro expanded files. - /// This only work for attribute expansions, as other ones do not have nodes as input. - pub fn original_ast_node(&self, node: N) -> Option { - self.imp.original_ast_node(node) - } - /// Attempts to map the node out of macro expanded files. - /// This only work for attribute expansions, as other ones do not have nodes as input. - pub fn original_syntax_node(&self, node: &SyntaxNode) -> Option { - self.imp.original_syntax_node(node) - } - - pub fn diagnostics_display_range(&self, diagnostics: InFile) -> FileRange { - self.imp.diagnostics_display_range(diagnostics) - } - pub fn token_ancestors_with_macros( &self, token: SyntaxToken, @@ -293,19 +152,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { token.parent().into_iter().flat_map(move |it| self.ancestors_with_macros(it)) } - /// Iterates the ancestors of the given node, climbing up macro expansions while doing so. - pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator + '_ { - self.imp.ancestors_with_macros(node) - } - - pub fn ancestors_at_offset_with_macros( - &self, - node: &SyntaxNode, - offset: TextSize, - ) -> impl Iterator + '_ { - self.imp.ancestors_at_offset_with_macros(node, offset) - } - /// Find an AstNode by offset inside SyntaxNode, if it is inside *Macrofile*, /// search up until it is of the target AstNode type pub fn find_node_at_offset_with_macros( @@ -336,53 +182,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.descend_node_at_offset(node, offset).filter_map(|mut it| it.find_map(N::cast)) } - pub fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option { - self.imp.resolve_lifetime_param(lifetime) - } - - pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option
", @@ -100,9 +107,19 @@ fn write_header(out: &mut Buffer, class: &str, extra_content: Option, to out.push_buffer(extra); } if class.is_empty() { - write!(out, "
");
+        write!(
+            out,
+            "
",
+            if extra_classes.is_empty() { "" } else { " " },
+            extra_classes.join(" "),
+        );
     } else {
-        write!(out, "
");
+        write!(
+            out,
+            "
",
+            if extra_classes.is_empty() { "" } else { " " },
+            extra_classes.join(" "),
+        );
     }
     write!(out, "");
 }
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index b28019e3f91..a25a6f7d35d 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -37,8 +37,9 @@ use once_cell::sync::Lazy;
 use std::borrow::Cow;
 use std::collections::VecDeque;
 use std::fmt::Write;
+use std::iter::Peekable;
 use std::ops::{ControlFlow, Range};
-use std::str;
+use std::str::{self, CharIndices};
 
 use crate::clean::RenderedLink;
 use crate::doctest;
@@ -243,11 +244,21 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> {
                 let parse_result =
                     LangString::parse_without_check(lang, self.check_error_codes, false);
                 if !parse_result.rust {
+                    let added_classes = parse_result.added_classes;
+                    let lang_string = if let Some(lang) = parse_result.unknown.first() {
+                        format!("language-{}", lang)
+                    } else {
+                        String::new()
+                    };
+                    let whitespace = if added_classes.is_empty() { "" } else { " " };
                     return Some(Event::Html(
                         format!(
                             "
\ -
{text}
\ +
\
+                                     {text}\
+                                 
\
", + added_classes = added_classes.join(" "), text = Escape(&original_text), ) .into(), @@ -258,6 +269,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { CodeBlockKind::Indented => Default::default(), }; + let added_classes = parse_result.added_classes; let lines = original_text.lines().filter_map(|l| map_line(l).for_html()); let text = lines.intersperse("\n".into()).collect::(); @@ -315,6 +327,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { &mut s, tooltip, playground_button.as_deref(), + &added_classes, ); Some(Event::Html(s.into_inner().into())) } @@ -711,6 +724,17 @@ pub(crate) fn find_testable_code( error_codes: ErrorCodes, enable_per_target_ignores: bool, extra_info: Option<&ExtraInfo<'_>>, +) { + find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false) +} + +pub(crate) fn find_codes( + doc: &str, + tests: &mut T, + error_codes: ErrorCodes, + enable_per_target_ignores: bool, + extra_info: Option<&ExtraInfo<'_>>, + include_non_rust: bool, ) { let mut parser = Parser::new(doc).into_offset_iter(); let mut prev_offset = 0; @@ -734,7 +758,7 @@ pub(crate) fn find_testable_code( } CodeBlockKind::Indented => Default::default(), }; - if !block_info.rust { + if !include_non_rust && !block_info.rust { continue; } @@ -784,7 +808,19 @@ impl<'tcx> ExtraInfo<'tcx> { ExtraInfo { def_id, sp, tcx } } - fn error_invalid_codeblock_attr(&self, msg: String, help: &'static str) { + fn error_invalid_codeblock_attr(&self, msg: &str) { + if let Some(def_id) = self.def_id.as_local() { + self.tcx.struct_span_lint_hir( + crate::lint::INVALID_CODEBLOCK_ATTRIBUTES, + self.tcx.hir().local_def_id_to_hir_id(def_id), + self.sp, + msg, + |l| l, + ); + } + } + + fn error_invalid_codeblock_attr_with_help(&self, msg: &str, help: &str) { if let Some(def_id) = self.def_id.as_local() { self.tcx.struct_span_lint_hir( crate::lint::INVALID_CODEBLOCK_ATTRIBUTES, @@ -808,6 +844,8 @@ pub(crate) struct LangString { pub(crate) compile_fail: bool, pub(crate) error_codes: Vec, pub(crate) edition: Option, + pub(crate) added_classes: Vec, + pub(crate) unknown: Vec, } #[derive(Eq, PartialEq, Clone, Debug)] @@ -817,6 +855,109 @@ pub(crate) enum Ignore { Some(Vec), } +pub(crate) struct TagIterator<'a, 'tcx> { + inner: Peekable>, + data: &'a str, + is_in_attribute_block: bool, + extra: Option<&'a ExtraInfo<'tcx>>, +} + +#[derive(Debug, PartialEq)] +pub(crate) enum TokenKind<'a> { + Token(&'a str), + Attribute(&'a str), +} + +fn is_separator(c: char) -> bool { + c == ' ' || c == ',' || c == '\t' +} + +impl<'a, 'tcx> TagIterator<'a, 'tcx> { + pub(crate) fn new(data: &'a str, extra: Option<&'a ExtraInfo<'tcx>>) -> Self { + Self { inner: data.char_indices().peekable(), data, extra, is_in_attribute_block: false } + } + + fn skip_separators(&mut self) -> Option { + while let Some((pos, c)) = self.inner.peek() { + if !is_separator(*c) { + return Some(*pos); + } + self.inner.next(); + } + None + } + + fn emit_error(&self, err: &str) { + if let Some(extra) = self.extra { + extra.error_invalid_codeblock_attr(err); + } + } +} + +impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> { + type Item = TokenKind<'a>; + + fn next(&mut self) -> Option { + let Some(start) = self.skip_separators() else { + if self.is_in_attribute_block { + self.emit_error("unclosed attribute block (`{}`): missing `}` at the end"); + } + return None; + }; + if self.is_in_attribute_block { + while let Some((pos, c)) = self.inner.next() { + if is_separator(c) { + return Some(TokenKind::Attribute(&self.data[start..pos])); + } else if c == '{' { + // There shouldn't be a nested block! + self.emit_error("unexpected `{` inside attribute block (`{}`)"); + let attr = &self.data[start..pos]; + if attr.is_empty() { + return self.next(); + } + self.inner.next(); + return Some(TokenKind::Attribute(attr)); + } else if c == '}' { + self.is_in_attribute_block = false; + let attr = &self.data[start..pos]; + if attr.is_empty() { + return self.next(); + } + return Some(TokenKind::Attribute(attr)); + } + } + // Unclosed attribute block! + self.emit_error("unclosed attribute block (`{}`): missing `}` at the end"); + let token = &self.data[start..]; + if token.is_empty() { None } else { Some(TokenKind::Attribute(token)) } + } else { + while let Some((pos, c)) = self.inner.next() { + if is_separator(c) { + return Some(TokenKind::Token(&self.data[start..pos])); + } else if c == '{' { + self.is_in_attribute_block = true; + let token = &self.data[start..pos]; + if token.is_empty() { + return self.next(); + } + return Some(TokenKind::Token(token)); + } else if c == '}' { + // We're not in a block so it shouldn't be there! + self.emit_error("unexpected `}` outside attribute block (`{}`)"); + let token = &self.data[start..pos]; + if token.is_empty() { + return self.next(); + } + self.inner.next(); + return Some(TokenKind::Attribute(token)); + } + } + let token = &self.data[start..]; + if token.is_empty() { None } else { Some(TokenKind::Token(token)) } + } + } +} + impl Default for LangString { fn default() -> Self { Self { @@ -829,50 +970,37 @@ impl Default for LangString { compile_fail: false, error_codes: Vec::new(), edition: None, + added_classes: Vec::new(), + unknown: Vec::new(), } } } +fn handle_class(class: &str, after: &str, data: &mut LangString, extra: Option<&ExtraInfo<'_>>) { + if class.is_empty() { + if let Some(extra) = extra { + extra.error_invalid_codeblock_attr(&format!("missing class name after `{after}`")); + } + } else { + data.added_classes.push(class.to_owned()); + } +} + impl LangString { fn parse_without_check( string: &str, allow_error_code_check: ErrorCodes, enable_per_target_ignores: bool, - ) -> LangString { + ) -> Self { Self::parse(string, allow_error_code_check, enable_per_target_ignores, None) } - fn tokens(string: &str) -> impl Iterator { - // Pandoc, which Rust once used for generating documentation, - // expects lang strings to be surrounded by `{}` and for each token - // to be proceeded by a `.`. Since some of these lang strings are still - // loose in the wild, we strip a pair of surrounding `{}` from the lang - // string and a leading `.` from each token. - - let string = string.trim(); - - let first = string.chars().next(); - let last = string.chars().last(); - - let string = if first == Some('{') && last == Some('}') { - &string[1..string.len() - 1] - } else { - string - }; - - string - .split(|c| c == ',' || c == ' ' || c == '\t') - .map(str::trim) - .map(|token| token.strip_prefix('.').unwrap_or(token)) - .filter(|token| !token.is_empty()) - } - fn parse( string: &str, allow_error_code_check: ErrorCodes, enable_per_target_ignores: bool, extra: Option<&ExtraInfo<'_>>, - ) -> LangString { + ) -> Self { let allow_error_code_check = allow_error_code_check.as_bool(); let mut seen_rust_tags = false; let mut seen_other_tags = false; @@ -881,43 +1009,45 @@ impl LangString { data.original = string.to_owned(); - for token in Self::tokens(string) { + for token in TagIterator::new(string, extra) { match token { - "should_panic" => { + TokenKind::Token("should_panic") => { data.should_panic = true; seen_rust_tags = !seen_other_tags; } - "no_run" => { + TokenKind::Token("no_run") => { data.no_run = true; seen_rust_tags = !seen_other_tags; } - "ignore" => { + TokenKind::Token("ignore") => { data.ignore = Ignore::All; seen_rust_tags = !seen_other_tags; } - x if x.starts_with("ignore-") => { + TokenKind::Token(x) if x.starts_with("ignore-") => { if enable_per_target_ignores { ignores.push(x.trim_start_matches("ignore-").to_owned()); seen_rust_tags = !seen_other_tags; } } - "rust" => { + TokenKind::Token("rust") => { data.rust = true; seen_rust_tags = true; } - "test_harness" => { + TokenKind::Token("test_harness") => { data.test_harness = true; seen_rust_tags = !seen_other_tags || seen_rust_tags; } - "compile_fail" => { + TokenKind::Token("compile_fail") => { data.compile_fail = true; seen_rust_tags = !seen_other_tags || seen_rust_tags; data.no_run = true; } - x if x.starts_with("edition") => { + TokenKind::Token(x) if x.starts_with("edition") => { data.edition = x[7..].parse::().ok(); } - x if allow_error_code_check && x.starts_with('E') && x.len() == 5 => { + TokenKind::Token(x) + if allow_error_code_check && x.starts_with('E') && x.len() == 5 => + { if x[1..].parse::().is_ok() { data.error_codes.push(x.to_owned()); seen_rust_tags = !seen_other_tags || seen_rust_tags; @@ -925,7 +1055,7 @@ impl LangString { seen_other_tags = true; } } - x if extra.is_some() => { + TokenKind::Token(x) if extra.is_some() => { let s = x.to_lowercase(); if let Some((flag, help)) = if s == "compile-fail" || s == "compile_fail" @@ -958,15 +1088,31 @@ impl LangString { None } { if let Some(extra) = extra { - extra.error_invalid_codeblock_attr( - format!("unknown attribute `{x}`. Did you mean `{flag}`?"), + extra.error_invalid_codeblock_attr_with_help( + &format!("unknown attribute `{}`. Did you mean `{}`?", x, flag), help, ); } } seen_other_tags = true; + data.unknown.push(x.to_owned()); + } + TokenKind::Token(x) => { + seen_other_tags = true; + data.unknown.push(x.to_owned()); + } + TokenKind::Attribute(attr) => { + seen_other_tags = true; + if let Some(class) = attr.strip_prefix('.') { + handle_class(class, ".", &mut data, extra); + } else if let Some(class) = attr.strip_prefix("class=") { + handle_class(class, "class=", &mut data, extra); + } else if let Some(extra) = extra { + extra.error_invalid_codeblock_attr(&format!( + "unsupported attribute `{attr}`" + )); + } } - _ => seen_other_tags = true, } } diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index db8504d15c7..2c9c95590ac 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -117,6 +117,30 @@ fn test_lang_string_parse() { edition: Some(Edition::Edition2018), ..Default::default() }); + t(LangString { + original: "class:test".into(), + added_classes: vec!["test".into()], + rust: false, + ..Default::default() + }); + t(LangString { + original: "rust,class:test".into(), + added_classes: vec!["test".into()], + rust: true, + ..Default::default() + }); + t(LangString { + original: "class:test:with:colon".into(), + added_classes: vec!["test:with:colon".into()], + rust: false, + ..Default::default() + }); + t(LangString { + original: "class:first,class:second".into(), + added_classes: vec!["first".into(), "second".into()], + rust: false, + ..Default::default() + }); } #[test] diff --git a/src/librustdoc/passes/check_custom_code_classes.rs b/src/librustdoc/passes/check_custom_code_classes.rs new file mode 100644 index 00000000000..246e7f8f331 --- /dev/null +++ b/src/librustdoc/passes/check_custom_code_classes.rs @@ -0,0 +1,77 @@ +//! NIGHTLY & UNSTABLE CHECK: custom_code_classes_in_docs +//! +//! This pass will produce errors when finding custom classes outside of +//! nightly + relevant feature active. + +use super::Pass; +use crate::clean::{Crate, Item}; +use crate::core::DocContext; +use crate::fold::DocFolder; +use crate::html::markdown::{find_codes, ErrorCodes, LangString}; + +use rustc_session::parse::feature_err; +use rustc_span::symbol::sym; + +pub(crate) const CHECK_CUSTOM_CODE_CLASSES: Pass = Pass { + name: "check-custom-code-classes", + run: check_custom_code_classes, + description: "check for custom code classes without the feature-gate enabled", +}; + +pub(crate) fn check_custom_code_classes(krate: Crate, cx: &mut DocContext<'_>) -> Crate { + let mut coll = CustomCodeClassLinter { cx }; + + coll.fold_crate(krate) +} + +struct CustomCodeClassLinter<'a, 'tcx> { + cx: &'a DocContext<'tcx>, +} + +impl<'a, 'tcx> DocFolder for CustomCodeClassLinter<'a, 'tcx> { + fn fold_item(&mut self, item: Item) -> Option { + look_for_custom_classes(&self.cx, &item); + Some(self.fold_item_recur(item)) + } +} + +#[derive(Debug)] +struct TestsWithCustomClasses { + custom_classes_found: Vec, +} + +impl crate::doctest::Tester for TestsWithCustomClasses { + fn add_test(&mut self, _: String, config: LangString, _: usize) { + self.custom_classes_found.extend(config.added_classes.into_iter()); + } +} + +pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item) { + if !item.item_id.is_local() { + // If non-local, no need to check anything. + return; + } + + let mut tests = TestsWithCustomClasses { custom_classes_found: vec![] }; + + let dox = item.attrs.collapsed_doc_value().unwrap_or_default(); + find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true); + + if !tests.custom_classes_found.is_empty() && !cx.tcx.features().custom_code_classes_in_docs { + feature_err( + &cx.tcx.sess.parse_sess, + sym::custom_code_classes_in_docs, + item.attr_span(cx.tcx), + "custom classes in code blocks are unstable", + ) + .note( + // This will list the wrong items to make them more easily searchable. + // To ensure the most correct hits, it adds back the 'class:' that was stripped. + &format!( + "found these custom classes: class={}", + tests.custom_classes_found.join(",class=") + ), + ) + .emit(); + } +} diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index bb678e33888..4eeaaa2bb70 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -35,6 +35,9 @@ pub(crate) use self::calculate_doc_coverage::CALCULATE_DOC_COVERAGE; mod lint; pub(crate) use self::lint::RUN_LINTS; +mod check_custom_code_classes; +pub(crate) use self::check_custom_code_classes::CHECK_CUSTOM_CODE_CLASSES; + /// A single pass over the cleaned documentation. /// /// Runs in the compiler context, so it has access to types and traits and the like. @@ -66,6 +69,7 @@ pub(crate) enum Condition { /// The full list of passes. pub(crate) const PASSES: &[Pass] = &[ + CHECK_CUSTOM_CODE_CLASSES, CHECK_DOC_TEST_VISIBILITY, STRIP_HIDDEN, STRIP_PRIVATE, @@ -79,6 +83,7 @@ pub(crate) const PASSES: &[Pass] = &[ /// The list of passes run by default. pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[ + ConditionalPass::always(CHECK_CUSTOM_CODE_CLASSES), ConditionalPass::always(COLLECT_TRAIT_IMPLS), ConditionalPass::always(CHECK_DOC_TEST_VISIBILITY), ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden), From f5561842e3dfc9adc8da4ba12b95514da4d99f00 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 25 Apr 2023 15:04:46 +0200 Subject: [PATCH 178/250] Add tests for `custom_code_classes_in_docs` feature --- src/librustdoc/html/markdown/tests.rs | 131 ++++++++++++++---- .../custom_code_classes_in_docs-warning.rs | 19 +++ ...custom_code_classes_in_docs-warning.stderr | 65 +++++++++ .../custom_code_classes_in_docs-warning2.rs | 13 ++ ...ustom_code_classes_in_docs-warning2.stderr | 17 +++ ...eature-gate-custom_code_classes_in_docs.rs | 5 + ...re-gate-custom_code_classes_in_docs.stderr | 15 ++ tests/rustdoc-ui/issues/issue-91713.stdout | 2 + tests/rustdoc/custom_code_classes.rs | 28 ++++ 9 files changed, 268 insertions(+), 27 deletions(-) create mode 100644 tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs create mode 100644 tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr create mode 100644 tests/rustdoc-ui/custom_code_classes_in_docs-warning2.rs create mode 100644 tests/rustdoc-ui/custom_code_classes_in_docs-warning2.stderr create mode 100644 tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs create mode 100644 tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr create mode 100644 tests/rustdoc/custom_code_classes.rs diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 2c9c95590ac..dd3d0ebac0c 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -1,5 +1,8 @@ use super::{find_testable_code, plain_text_summary, short_markdown_summary}; -use super::{ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, Markdown, MarkdownItemInfo}; +use super::{ + ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, Markdown, MarkdownItemInfo, TagIterator, + TokenKind, +}; use rustc_span::edition::{Edition, DEFAULT_EDITION}; #[test] @@ -51,10 +54,25 @@ fn test_lang_string_parse() { t(Default::default()); t(LangString { original: "rust".into(), ..Default::default() }); - t(LangString { original: ".rust".into(), ..Default::default() }); - t(LangString { original: "{rust}".into(), ..Default::default() }); - t(LangString { original: "{.rust}".into(), ..Default::default() }); - t(LangString { original: "sh".into(), rust: false, ..Default::default() }); + t(LangString { + original: ".rust".into(), + rust: false, + unknown: vec![".rust".into()], + ..Default::default() + }); + t(LangString { original: "{rust}".into(), rust: false, ..Default::default() }); + t(LangString { + original: "{.rust}".into(), + rust: false, + added_classes: vec!["rust".into()], + ..Default::default() + }); + t(LangString { + original: "sh".into(), + rust: false, + unknown: vec!["sh".into()], + ..Default::default() + }); t(LangString { original: "ignore".into(), ignore: Ignore::All, ..Default::default() }); t(LangString { original: "ignore-foo".into(), @@ -70,41 +88,56 @@ fn test_lang_string_parse() { compile_fail: true, ..Default::default() }); - t(LangString { original: "no_run,example".into(), no_run: true, ..Default::default() }); + t(LangString { + original: "no_run,example".into(), + no_run: true, + unknown: vec!["example".into()], + ..Default::default() + }); t(LangString { original: "sh,should_panic".into(), should_panic: true, rust: false, + unknown: vec!["sh".into()], + ..Default::default() + }); + t(LangString { + original: "example,rust".into(), + unknown: vec!["example".into()], ..Default::default() }); - t(LangString { original: "example,rust".into(), ..Default::default() }); t(LangString { original: "test_harness,.rust".into(), test_harness: true, + unknown: vec![".rust".into()], ..Default::default() }); t(LangString { original: "text, no_run".into(), no_run: true, rust: false, + unknown: vec!["text".into()], ..Default::default() }); t(LangString { original: "text,no_run".into(), no_run: true, rust: false, + unknown: vec!["text".into()], ..Default::default() }); t(LangString { original: "text,no_run, ".into(), no_run: true, rust: false, + unknown: vec!["text".into()], ..Default::default() }); t(LangString { original: "text,no_run,".into(), no_run: true, rust: false, + unknown: vec!["text".into()], ..Default::default() }); t(LangString { @@ -118,52 +151,96 @@ fn test_lang_string_parse() { ..Default::default() }); t(LangString { - original: "class:test".into(), + original: "{class=test}".into(), added_classes: vec!["test".into()], rust: false, ..Default::default() }); t(LangString { - original: "rust,class:test".into(), + original: "{.test}".into(), added_classes: vec!["test".into()], + rust: false, + ..Default::default() + }); + t(LangString { + original: "rust,{class=test,.test2}".into(), + added_classes: vec!["test".into(), "test2".into()], rust: true, ..Default::default() }); t(LangString { - original: "class:test:with:colon".into(), - added_classes: vec!["test:with:colon".into()], + original: "{class=test:with:colon .test1}".into(), + added_classes: vec!["test:with:colon".into(), "test1".into()], rust: false, ..Default::default() }); t(LangString { - original: "class:first,class:second".into(), + original: "{class=first,class=second}".into(), added_classes: vec!["first".into(), "second".into()], rust: false, ..Default::default() }); + t(LangString { + original: "{class=first,.second},unknown".into(), + added_classes: vec!["first".into(), "second".into()], + rust: false, + unknown: vec!["unknown".into()], + ..Default::default() + }); + t(LangString { + original: "{class=first .second} unknown".into(), + added_classes: vec!["first".into(), "second".into()], + rust: false, + unknown: vec!["unknown".into()], + ..Default::default() + }); + t(LangString { + original: "{.first.second}".into(), + added_classes: vec!["first.second".into()], + rust: false, + ..Default::default() + }); + t(LangString { + original: "{class=first=second}".into(), + added_classes: vec!["first=second".into()], + rust: false, + ..Default::default() + }); + t(LangString { + original: "{class=first.second}".into(), + added_classes: vec!["first.second".into()], + rust: false, + ..Default::default() + }); + t(LangString { + original: "{class=.first}".into(), + added_classes: vec![".first".into()], + rust: false, + ..Default::default() + }); } #[test] fn test_lang_string_tokenizer() { - fn case(lang_string: &str, want: &[&str]) { - let have = LangString::tokens(lang_string).collect::>(); + fn case(lang_string: &str, want: &[TokenKind<'_>]) { + let have = TagIterator::new(lang_string, None).collect::>(); assert_eq!(have, want, "Unexpected lang string split for `{}`", lang_string); } case("", &[]); - case("foo", &["foo"]); - case("foo,bar", &["foo", "bar"]); - case(".foo,.bar", &["foo", "bar"]); - case("{.foo,.bar}", &["foo", "bar"]); - case(" {.foo,.bar} ", &["foo", "bar"]); - case("foo bar", &["foo", "bar"]); - case("foo\tbar", &["foo", "bar"]); - case("foo\t, bar", &["foo", "bar"]); - case(" foo , bar ", &["foo", "bar"]); - case(",,foo,,bar,,", &["foo", "bar"]); - case("foo=bar", &["foo=bar"]); - case("a-b-c", &["a-b-c"]); - case("a_b_c", &["a_b_c"]); + case("foo", &[TokenKind::Token("foo")]); + case("foo,bar", &[TokenKind::Token("foo"), TokenKind::Token("bar")]); + case(".foo,.bar", &[TokenKind::Token(".foo"), TokenKind::Token(".bar")]); + case("{.foo,.bar}", &[TokenKind::Attribute(".foo"), TokenKind::Attribute(".bar")]); + case(" {.foo,.bar} ", &[TokenKind::Attribute(".foo"), TokenKind::Attribute(".bar")]); + case("foo bar", &[TokenKind::Token("foo"), TokenKind::Token("bar")]); + case("foo\tbar", &[TokenKind::Token("foo"), TokenKind::Token("bar")]); + case("foo\t, bar", &[TokenKind::Token("foo"), TokenKind::Token("bar")]); + case(" foo , bar ", &[TokenKind::Token("foo"), TokenKind::Token("bar")]); + case(",,foo,,bar,,", &[TokenKind::Token("foo"), TokenKind::Token("bar")]); + case("foo=bar", &[TokenKind::Token("foo=bar")]); + case("a-b-c", &[TokenKind::Token("a-b-c")]); + case("a_b_c", &[TokenKind::Token("a_b_c")]); } #[test] diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs new file mode 100644 index 00000000000..c28921b01f1 --- /dev/null +++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs @@ -0,0 +1,19 @@ +// This test ensures that warnings are working as expected for "custom_code_classes_in_docs" +// feature. + +#![feature(custom_code_classes_in_docs)] +#![deny(warnings)] +#![feature(no_core)] +#![no_core] + +/// ```{. class= whatever=hehe #id} } {{ +/// main; +/// ``` +//~^^^ ERROR missing class name after `.` +//~| ERROR missing class name after `class=` +//~| ERROR unsupported attribute `whatever=hehe` +//~| ERROR unsupported attribute `#id` +//~| ERROR unexpected `}` outside attribute block (`{}`) +//~| ERROR unclosed attribute block (`{}`): missing `}` at the end +//~| ERROR unexpected `{` inside attribute block (`{}`) +pub fn foo() {} diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr new file mode 100644 index 00000000000..f19b62914db --- /dev/null +++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr @@ -0,0 +1,65 @@ +error: missing class name after `.` + --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 + | +LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | | /// main; +LL | | /// ``` + | |_______^ + | +note: the lint level is defined here + --> $DIR/custom_code_classes_in_docs-warning.rs:5:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]` + +error: missing class name after `class=` + --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 + | +LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | | /// main; +LL | | /// ``` + | |_______^ + +error: unsupported attribute `whatever=hehe` + --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 + | +LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | | /// main; +LL | | /// ``` + | |_______^ + +error: unsupported attribute `#id` + --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 + | +LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | | /// main; +LL | | /// ``` + | |_______^ + +error: unexpected `}` outside attribute block (`{}`) + --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 + | +LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | | /// main; +LL | | /// ``` + | |_______^ + +error: unexpected `{` inside attribute block (`{}`) + --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 + | +LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | | /// main; +LL | | /// ``` + | |_______^ + +error: unclosed attribute block (`{}`): missing `}` at the end + --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 + | +LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | | /// main; +LL | | /// ``` + | |_______^ + +error: aborting due to 7 previous errors + diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning2.rs b/tests/rustdoc-ui/custom_code_classes_in_docs-warning2.rs new file mode 100644 index 00000000000..b2ce7407ec6 --- /dev/null +++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning2.rs @@ -0,0 +1,13 @@ +// This test ensures that warnings are working as expected for "custom_code_classes_in_docs" +// feature. + +#![feature(custom_code_classes_in_docs)] +#![deny(warnings)] +#![feature(no_core)] +#![no_core] + +/// ```{class=} +/// main; +/// ``` +//~^^^ ERROR missing class name after `class=` +pub fn foo() {} diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning2.stderr b/tests/rustdoc-ui/custom_code_classes_in_docs-warning2.stderr new file mode 100644 index 00000000000..52bb1dae9f6 --- /dev/null +++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning2.stderr @@ -0,0 +1,17 @@ +error: missing class name after `class=` + --> $DIR/custom_code_classes_in_docs-warning2.rs:9:1 + | +LL | / /// ```{class=} +LL | | /// main; +LL | | /// ``` + | |_______^ + | +note: the lint level is defined here + --> $DIR/custom_code_classes_in_docs-warning2.rs:5:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]` + +error: aborting due to previous error + diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs new file mode 100644 index 00000000000..8aa13b2d5d1 --- /dev/null +++ b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs @@ -0,0 +1,5 @@ +/// ```{class=language-c} +/// int main(void) { return 0; } +/// ``` +//~^^^ ERROR 1:1: 3:8: custom classes in code blocks are unstable [E0658] +pub struct Bar; diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr new file mode 100644 index 00000000000..c41ebfc8073 --- /dev/null +++ b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr @@ -0,0 +1,15 @@ +error[E0658]: custom classes in code blocks are unstable + --> $DIR/feature-gate-custom_code_classes_in_docs.rs:1:1 + | +LL | / /// ```{class=language-c} +LL | | /// int main(void) { return 0; } +LL | | /// ``` + | |_______^ + | + = note: see issue #79483 for more information + = help: add `#![feature(custom_code_classes_in_docs)]` to the crate attributes to enable + = note: found these custom classes: class=language-c + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/rustdoc-ui/issues/issue-91713.stdout b/tests/rustdoc-ui/issues/issue-91713.stdout index 16783524363..bbea7e5c212 100644 --- a/tests/rustdoc-ui/issues/issue-91713.stdout +++ b/tests/rustdoc-ui/issues/issue-91713.stdout @@ -1,4 +1,5 @@ Available passes for running rustdoc: +check-custom-code-classes - check for custom code classes without the feature-gate enabled check_doc_test_visibility - run various visibility-related lints on doctests strip-hidden - strips all `#[doc(hidden)]` items from the output strip-private - strips all private items from a crate which cannot be seen externally, implies strip-priv-imports @@ -10,6 +11,7 @@ calculate-doc-coverage - counts the number of items with and without documentati run-lints - runs some of rustdoc's lints Default passes for rustdoc: +check-custom-code-classes collect-trait-impls check_doc_test_visibility strip-hidden (when not --document-hidden-items) diff --git a/tests/rustdoc/custom_code_classes.rs b/tests/rustdoc/custom_code_classes.rs new file mode 100644 index 00000000000..f110721c5a7 --- /dev/null +++ b/tests/rustdoc/custom_code_classes.rs @@ -0,0 +1,28 @@ +// Test for `custom_code_classes_in_docs` feature. + +#![feature(custom_code_classes_in_docs)] +#![crate_name = "foo"] +#![feature(no_core)] +#![no_core] + +// @has 'foo/struct.Bar.html' +// @has - '//*[@id="main-content"]//pre[@class="language-whatever hoho-c"]' 'main;' +// @has - '//*[@id="main-content"]//pre[@class="language-whatever2 haha-c"]' 'main;' +// @has - '//*[@id="main-content"]//pre[@class="language-whatever4 huhu-c"]' 'main;' + +/// ```{class=hoho-c},whatever +/// main; +/// ``` +/// +/// Testing multiple kinds of orders. +/// +/// ```whatever2 {class=haha-c} +/// main; +/// ``` +/// +/// Testing with multiple "unknown". Only the first should be used. +/// +/// ```whatever4{.huhu-c} whatever5 +/// main; +/// ``` +pub struct Bar; From d829fee6b5859de516dadaaba30db758bb567268 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 25 Apr 2023 16:37:43 +0200 Subject: [PATCH 179/250] Add documentation for `custom_code_classes_in_docs` feature --- src/doc/rustdoc/src/unstable-features.md | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index f69156b7c05..bb62a0bc9cc 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -625,3 +625,32 @@ and check the values of `feature`: `foo` and `bar`. This flag enables the generation of links in the source code pages which allow the reader to jump to a type definition. + +### Custom CSS classes for code blocks + +```rust +#![feature(custom_code_classes_in_docs)] + +/// ```{class=language-c} +/// int main(void) { return 0; } +/// ``` +pub struct Bar; +``` + +The text `int main(void) { return 0; }` is rendered without highlighting in a code block +with the class `language-c`. This can be used to highlight other languages through JavaScript +libraries for example. + +To be noted that you can replace `class=` with `.` to achieve the same result: + +```rust +#![feature(custom_code_classes_in_docs)] + +/// ```{.language-c} +/// int main(void) { return 0; } +/// ``` +pub struct Bar; +``` + +To be noted, `rust` and `.rust`/`class=rust` have different effects: `rust` indicates that this is +a Rust code block whereas the two others add a "rust" CSS class on the code block. From 4ce17fa30eeef32235e9b305f56b5651c6d35276 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 27 Apr 2023 15:09:43 +0200 Subject: [PATCH 180/250] Add support for double quotes in markdown codeblock attributes --- src/doc/rustdoc/src/unstable-features.md | 11 ++ src/librustdoc/html/markdown.rs | 121 +++++++++++------- src/librustdoc/html/markdown/tests.rs | 12 ++ .../custom_code_classes_in_docs-warning3.rs | 17 +++ ...ustom_code_classes_in_docs-warning3.stderr | 33 +++++ 5 files changed, 145 insertions(+), 49 deletions(-) create mode 100644 tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs create mode 100644 tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index bb62a0bc9cc..b5d8c819418 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -654,3 +654,14 @@ pub struct Bar; To be noted, `rust` and `.rust`/`class=rust` have different effects: `rust` indicates that this is a Rust code block whereas the two others add a "rust" CSS class on the code block. + +You can also use double quotes: + +```rust +#![feature(custom_code_classes_in_docs)] + +/// ```"not rust" {."hello everyone"} +/// int main(void) { return 0; } +/// ``` +pub struct Bar; +``` diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index a25a6f7d35d..6bd4e775c0e 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -892,6 +892,75 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> { extra.error_invalid_codeblock_attr(err); } } + + /// Returns false if the string is unfinished. + fn skip_string(&mut self) -> bool { + while let Some((_, c)) = self.inner.next() { + if c == '"' { + return true; + } + } + self.emit_error("unclosed quote string: missing `\"` at the end"); + false + } + + fn parse_in_attribute_block(&mut self, start: usize) -> Option> { + while let Some((pos, c)) = self.inner.next() { + if is_separator(c) { + return Some(TokenKind::Attribute(&self.data[start..pos])); + } else if c == '{' { + // There shouldn't be a nested block! + self.emit_error("unexpected `{` inside attribute block (`{}`)"); + let attr = &self.data[start..pos]; + if attr.is_empty() { + return self.next(); + } + self.inner.next(); + return Some(TokenKind::Attribute(attr)); + } else if c == '}' { + self.is_in_attribute_block = false; + let attr = &self.data[start..pos]; + if attr.is_empty() { + return self.next(); + } + return Some(TokenKind::Attribute(attr)); + } else if c == '"' && !self.skip_string() { + return None; + } + } + // Unclosed attribute block! + self.emit_error("unclosed attribute block (`{}`): missing `}` at the end"); + let token = &self.data[start..]; + if token.is_empty() { None } else { Some(TokenKind::Attribute(token)) } + } + + fn parse_outside_attribute_block(&mut self, start: usize) -> Option> { + while let Some((pos, c)) = self.inner.next() { + if is_separator(c) { + return Some(TokenKind::Token(&self.data[start..pos])); + } else if c == '{' { + self.is_in_attribute_block = true; + let token = &self.data[start..pos]; + if token.is_empty() { + return self.next(); + } + return Some(TokenKind::Token(token)); + } else if c == '}' { + // We're not in a block so it shouldn't be there! + self.emit_error("unexpected `}` outside attribute block (`{}`)"); + let token = &self.data[start..pos]; + if token.is_empty() { + return self.next(); + } + self.inner.next(); + return Some(TokenKind::Attribute(token)); + } else if c == '"' && !self.skip_string() { + return None; + } + } + let token = &self.data[start..]; + if token.is_empty() { None } else { Some(TokenKind::Token(token)) } + } } impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> { @@ -905,55 +974,9 @@ impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> { return None; }; if self.is_in_attribute_block { - while let Some((pos, c)) = self.inner.next() { - if is_separator(c) { - return Some(TokenKind::Attribute(&self.data[start..pos])); - } else if c == '{' { - // There shouldn't be a nested block! - self.emit_error("unexpected `{` inside attribute block (`{}`)"); - let attr = &self.data[start..pos]; - if attr.is_empty() { - return self.next(); - } - self.inner.next(); - return Some(TokenKind::Attribute(attr)); - } else if c == '}' { - self.is_in_attribute_block = false; - let attr = &self.data[start..pos]; - if attr.is_empty() { - return self.next(); - } - return Some(TokenKind::Attribute(attr)); - } - } - // Unclosed attribute block! - self.emit_error("unclosed attribute block (`{}`): missing `}` at the end"); - let token = &self.data[start..]; - if token.is_empty() { None } else { Some(TokenKind::Attribute(token)) } + self.parse_in_attribute_block(start) } else { - while let Some((pos, c)) = self.inner.next() { - if is_separator(c) { - return Some(TokenKind::Token(&self.data[start..pos])); - } else if c == '{' { - self.is_in_attribute_block = true; - let token = &self.data[start..pos]; - if token.is_empty() { - return self.next(); - } - return Some(TokenKind::Token(token)); - } else if c == '}' { - // We're not in a block so it shouldn't be there! - self.emit_error("unexpected `}` outside attribute block (`{}`)"); - let token = &self.data[start..pos]; - if token.is_empty() { - return self.next(); - } - self.inner.next(); - return Some(TokenKind::Attribute(token)); - } - } - let token = &self.data[start..]; - if token.is_empty() { None } else { Some(TokenKind::Token(token)) } + self.parse_outside_attribute_block(start) } } } @@ -982,7 +1005,7 @@ fn handle_class(class: &str, after: &str, data: &mut LangString, extra: Option<& extra.error_invalid_codeblock_attr(&format!("missing class name after `{after}`")); } } else { - data.added_classes.push(class.to_owned()); + data.added_classes.push(class.replace('"', "")); } } diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index dd3d0ebac0c..b0b4de65cca 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -218,6 +218,18 @@ fn test_lang_string_parse() { rust: false, ..Default::default() }); + t(LangString { + original: r#"{class="first"}"#.into(), + added_classes: vec!["first".into()], + rust: false, + ..Default::default() + }); + t(LangString { + original: r#"{class=f"irst"}"#.into(), + added_classes: vec!["first".into()], + rust: false, + ..Default::default() + }); } #[test] diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs new file mode 100644 index 00000000000..57d9038cb0c --- /dev/null +++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs @@ -0,0 +1,17 @@ +// This test ensures that warnings are working as expected for "custom_code_classes_in_docs" +// feature. + +#![feature(custom_code_classes_in_docs)] +#![deny(warnings)] +#![feature(no_core)] +#![no_core] + +/// ```{class="} +/// main; +/// ``` +//~^^^ ERROR unclosed quote string +//~| ERROR unclosed quote string +/// ```" +/// main; +/// ``` +pub fn foo() {} diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr new file mode 100644 index 00000000000..7432af19360 --- /dev/null +++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr @@ -0,0 +1,33 @@ +error: unclosed quote string: missing `"` at the end + --> $DIR/custom_code_classes_in_docs-warning3.rs:9:1 + | +LL | / /// ```{class="} +LL | | /// main; +LL | | /// ``` +LL | | +... | +LL | | /// main; +LL | | /// ``` + | |_______^ + | +note: the lint level is defined here + --> $DIR/custom_code_classes_in_docs-warning3.rs:5:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]` + +error: unclosed quote string: missing `"` at the end + --> $DIR/custom_code_classes_in_docs-warning3.rs:9:1 + | +LL | / /// ```{class="} +LL | | /// main; +LL | | /// ``` +LL | | +... | +LL | | /// main; +LL | | /// ``` + | |_______^ + +error: aborting due to 2 previous errors + From 7681f63cab88109dd8d445cf7b49cbbd63f81e75 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 2 May 2023 22:01:28 +0200 Subject: [PATCH 181/250] Implement new eBNF for codeblock attributes --- src/librustdoc/html/markdown.rs | 278 ++++++++++++------ src/librustdoc/html/markdown/tests.rs | 89 +++--- .../custom_code_classes_in_docs-warning.rs | 82 +++++- ...custom_code_classes_in_docs-warning.stderr | 90 ++++-- .../custom_code_classes_in_docs-warning2.rs | 13 - ...ustom_code_classes_in_docs-warning2.stderr | 17 -- ...ustom_code_classes_in_docs-warning3.stderr | 4 +- tests/rustdoc/custom_code_classes.rs | 2 +- 8 files changed, 376 insertions(+), 199 deletions(-) delete mode 100644 tests/rustdoc-ui/custom_code_classes_in_docs-warning2.rs delete mode 100644 tests/rustdoc-ui/custom_code_classes_in_docs-warning2.stderr diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 6bd4e775c0e..0a741f7815b 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -862,19 +862,34 @@ pub(crate) struct TagIterator<'a, 'tcx> { extra: Option<&'a ExtraInfo<'tcx>>, } -#[derive(Debug, PartialEq)] -pub(crate) enum TokenKind<'a> { - Token(&'a str), - Attribute(&'a str), +#[derive(Clone, Debug, Eq, PartialEq)] +pub(crate) enum LangStringToken<'a> { + LangToken(&'a str), + ClassAttribute(&'a str), + KeyValueAttribute(&'a str, &'a str), } +fn is_bareword_char(c: char) -> bool { + c == '_' || c == '-' || c == ':' || c.is_ascii_alphabetic() || c.is_ascii_digit() +} fn is_separator(c: char) -> bool { c == ' ' || c == ',' || c == '\t' } +struct Indices { + start: usize, + end: usize, +} + impl<'a, 'tcx> TagIterator<'a, 'tcx> { pub(crate) fn new(data: &'a str, extra: Option<&'a ExtraInfo<'tcx>>) -> Self { - Self { inner: data.char_indices().peekable(), data, extra, is_in_attribute_block: false } + Self { inner: data.char_indices().peekable(), data, is_in_attribute_block: false, extra } + } + + fn emit_error(&self, err: &str) { + if let Some(extra) = self.extra { + extra.error_invalid_codeblock_attr(err); + } } fn skip_separators(&mut self) -> Option { @@ -887,84 +902,183 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> { None } - fn emit_error(&self, err: &str) { - if let Some(extra) = self.extra { - extra.error_invalid_codeblock_attr(err); + fn parse_string(&mut self, start: usize) -> Option { + while let Some((pos, c)) = self.inner.next() { + if c == '"' { + return Some(Indices { start: start + 1, end: pos }); + } + } + self.emit_error("unclosed quote string `\"`"); + None + } + + fn parse_class(&mut self, start: usize) -> Option> { + while let Some((pos, c)) = self.inner.peek().copied() { + if is_bareword_char(c) { + self.inner.next(); + } else { + let class = &self.data[start + 1..pos]; + if class.is_empty() { + self.emit_error(&format!("unexpected `{c}` character after `.`")); + return None; + } else if self.check_after_token() { + return Some(LangStringToken::ClassAttribute(class)); + } else { + return None; + } + } + } + let class = &self.data[start + 1..]; + if class.is_empty() { + self.emit_error("missing character after `.`"); + None + } else if self.check_after_token() { + Some(LangStringToken::ClassAttribute(class)) + } else { + None } } - /// Returns false if the string is unfinished. - fn skip_string(&mut self) -> bool { + fn parse_token(&mut self, start: usize) -> Option { + while let Some((pos, c)) = self.inner.peek() { + if !is_bareword_char(*c) { + return Some(Indices { start, end: *pos }); + } + self.inner.next(); + } + self.emit_error("unexpected end"); + None + } + + fn parse_key_value(&mut self, c: char, start: usize) -> Option> { + let key_indices = + if c == '"' { self.parse_string(start)? } else { self.parse_token(start)? }; + if key_indices.start == key_indices.end { + self.emit_error("unexpected empty string as key"); + return None; + } + + if let Some((_, c)) = self.inner.next() { + if c != '=' { + self.emit_error(&format!("expected `=`, found `{}`", c)); + return None; + } + } else { + self.emit_error("unexpected end"); + return None; + } + let value_indices = match self.inner.next() { + Some((pos, '"')) => self.parse_string(pos)?, + Some((pos, c)) if is_bareword_char(c) => self.parse_token(pos)?, + Some((_, c)) => { + self.emit_error(&format!("unexpected `{c}` character after `=`")); + return None; + } + None => { + self.emit_error("expected value after `=`"); + return None; + } + }; + if value_indices.start == value_indices.end { + self.emit_error("unexpected empty string as value"); + None + } else if self.check_after_token() { + Some(LangStringToken::KeyValueAttribute( + &self.data[key_indices.start..key_indices.end], + &self.data[value_indices.start..value_indices.end], + )) + } else { + None + } + } + + /// Returns `false` if an error was emitted. + fn check_after_token(&mut self) -> bool { + if let Some((_, c)) = self.inner.peek().copied() { + if c == '}' || is_separator(c) || c == '(' { + true + } else { + self.emit_error(&format!("unexpected `{c}` character")); + false + } + } else { + // The error will be caught on the next iteration. + true + } + } + + fn parse_in_attribute_block(&mut self) -> Option> { + while let Some((pos, c)) = self.inner.next() { + if c == '}' { + self.is_in_attribute_block = false; + return self.next(); + } else if c == '.' { + return self.parse_class(pos); + } else if c == '"' || is_bareword_char(c) { + return self.parse_key_value(c, pos); + } else { + self.emit_error(&format!("unexpected character `{c}`")); + return None; + } + } + self.emit_error("unclosed attribute block (`{}`): missing `}` at the end"); + None + } + + /// Returns `false` if an error was emitted. + fn skip_paren_block(&mut self) -> bool { while let Some((_, c)) = self.inner.next() { - if c == '"' { + if c == ')' { return true; } } - self.emit_error("unclosed quote string: missing `\"` at the end"); + self.emit_error("unclosed comment: missing `)` at the end"); false } - fn parse_in_attribute_block(&mut self, start: usize) -> Option> { + fn parse_outside_attribute_block(&mut self, start: usize) -> Option> { while let Some((pos, c)) = self.inner.next() { - if is_separator(c) { - return Some(TokenKind::Attribute(&self.data[start..pos])); - } else if c == '{' { - // There shouldn't be a nested block! - self.emit_error("unexpected `{` inside attribute block (`{}`)"); - let attr = &self.data[start..pos]; - if attr.is_empty() { - return self.next(); + if c == '"' { + if pos != start { + self.emit_error("expected ` `, `{` or `,` found `\"`"); + return None; } - self.inner.next(); - return Some(TokenKind::Attribute(attr)); - } else if c == '}' { - self.is_in_attribute_block = false; - let attr = &self.data[start..pos]; - if attr.is_empty() { - return self.next(); + let indices = self.parse_string(pos)?; + if let Some((_, c)) = self.inner.peek().copied() && c != '{' && !is_separator(c) && c != '(' { + self.emit_error(&format!("expected ` `, `{{` or `,` after `\"`, found `{c}`")); + return None; } - return Some(TokenKind::Attribute(attr)); - } else if c == '"' && !self.skip_string() { - return None; - } - } - // Unclosed attribute block! - self.emit_error("unclosed attribute block (`{}`): missing `}` at the end"); - let token = &self.data[start..]; - if token.is_empty() { None } else { Some(TokenKind::Attribute(token)) } - } - - fn parse_outside_attribute_block(&mut self, start: usize) -> Option> { - while let Some((pos, c)) = self.inner.next() { - if is_separator(c) { - return Some(TokenKind::Token(&self.data[start..pos])); + return Some(LangStringToken::LangToken(&self.data[indices.start..indices.end])); } else if c == '{' { self.is_in_attribute_block = true; - let token = &self.data[start..pos]; - if token.is_empty() { - return self.next(); + return self.next(); + } else if is_bareword_char(c) { + continue; + } else if is_separator(c) { + if pos != start { + return Some(LangStringToken::LangToken(&self.data[start..pos])); } - return Some(TokenKind::Token(token)); - } else if c == '}' { - // We're not in a block so it shouldn't be there! - self.emit_error("unexpected `}` outside attribute block (`{}`)"); - let token = &self.data[start..pos]; - if token.is_empty() { - return self.next(); + return self.next(); + } else if c == '(' { + if !self.skip_paren_block() { + return None; } - self.inner.next(); - return Some(TokenKind::Attribute(token)); - } else if c == '"' && !self.skip_string() { + if pos != start { + return Some(LangStringToken::LangToken(&self.data[start..pos])); + } + return self.next(); + } else { + self.emit_error(&format!("unexpected character `{c}`")); return None; } } let token = &self.data[start..]; - if token.is_empty() { None } else { Some(TokenKind::Token(token)) } + if token.is_empty() { None } else { Some(LangStringToken::LangToken(&self.data[start..])) } } } impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> { - type Item = TokenKind<'a>; + type Item = LangStringToken<'a>; fn next(&mut self) -> Option { let Some(start) = self.skip_separators() else { @@ -974,7 +1088,7 @@ impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> { return None; }; if self.is_in_attribute_block { - self.parse_in_attribute_block(start) + self.parse_in_attribute_block() } else { self.parse_outside_attribute_block(start) } @@ -999,16 +1113,6 @@ impl Default for LangString { } } -fn handle_class(class: &str, after: &str, data: &mut LangString, extra: Option<&ExtraInfo<'_>>) { - if class.is_empty() { - if let Some(extra) = extra { - extra.error_invalid_codeblock_attr(&format!("missing class name after `{after}`")); - } - } else { - data.added_classes.push(class.replace('"', "")); - } -} - impl LangString { fn parse_without_check( string: &str, @@ -1034,41 +1138,41 @@ impl LangString { for token in TagIterator::new(string, extra) { match token { - TokenKind::Token("should_panic") => { + LangStringToken::LangToken("should_panic") => { data.should_panic = true; seen_rust_tags = !seen_other_tags; } - TokenKind::Token("no_run") => { + LangStringToken::LangToken("no_run") => { data.no_run = true; seen_rust_tags = !seen_other_tags; } - TokenKind::Token("ignore") => { + LangStringToken::LangToken("ignore") => { data.ignore = Ignore::All; seen_rust_tags = !seen_other_tags; } - TokenKind::Token(x) if x.starts_with("ignore-") => { + LangStringToken::LangToken(x) if x.starts_with("ignore-") => { if enable_per_target_ignores { ignores.push(x.trim_start_matches("ignore-").to_owned()); seen_rust_tags = !seen_other_tags; } } - TokenKind::Token("rust") => { + LangStringToken::LangToken("rust") => { data.rust = true; seen_rust_tags = true; } - TokenKind::Token("test_harness") => { + LangStringToken::LangToken("test_harness") => { data.test_harness = true; seen_rust_tags = !seen_other_tags || seen_rust_tags; } - TokenKind::Token("compile_fail") => { + LangStringToken::LangToken("compile_fail") => { data.compile_fail = true; seen_rust_tags = !seen_other_tags || seen_rust_tags; data.no_run = true; } - TokenKind::Token(x) if x.starts_with("edition") => { + LangStringToken::LangToken(x) if x.starts_with("edition") => { data.edition = x[7..].parse::().ok(); } - TokenKind::Token(x) + LangStringToken::LangToken(x) if allow_error_code_check && x.starts_with('E') && x.len() == 5 => { if x[1..].parse::().is_ok() { @@ -1078,7 +1182,7 @@ impl LangString { seen_other_tags = true; } } - TokenKind::Token(x) if extra.is_some() => { + LangStringToken::LangToken(x) if extra.is_some() => { let s = x.to_lowercase(); if let Some((flag, help)) = if s == "compile-fail" || s == "compile_fail" @@ -1120,22 +1224,24 @@ impl LangString { seen_other_tags = true; data.unknown.push(x.to_owned()); } - TokenKind::Token(x) => { + LangStringToken::LangToken(x) => { seen_other_tags = true; data.unknown.push(x.to_owned()); } - TokenKind::Attribute(attr) => { + LangStringToken::KeyValueAttribute(key, value) => { seen_other_tags = true; - if let Some(class) = attr.strip_prefix('.') { - handle_class(class, ".", &mut data, extra); - } else if let Some(class) = attr.strip_prefix("class=") { - handle_class(class, "class=", &mut data, extra); + if key == "class" { + data.added_classes.push(value.to_owned()); } else if let Some(extra) = extra { extra.error_invalid_codeblock_attr(&format!( - "unsupported attribute `{attr}`" + "unsupported attribute `{key}`" )); } } + LangStringToken::ClassAttribute(class) => { + seen_other_tags = true; + data.added_classes.push(class.to_owned()); + } } } diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index b0b4de65cca..35b243d3d29 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -1,7 +1,7 @@ use super::{find_testable_code, plain_text_summary, short_markdown_summary}; use super::{ - ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, Markdown, MarkdownItemInfo, TagIterator, - TokenKind, + ErrorCodes, HeadingOffset, IdMap, Ignore, LangString, LangStringToken, Markdown, + MarkdownItemInfo, TagIterator, }; use rustc_span::edition::{Edition, DEFAULT_EDITION}; @@ -55,12 +55,13 @@ fn test_lang_string_parse() { t(Default::default()); t(LangString { original: "rust".into(), ..Default::default() }); t(LangString { - original: ".rust".into(), + original: "rusta".into(), rust: false, - unknown: vec![".rust".into()], + unknown: vec!["rusta".into()], ..Default::default() }); - t(LangString { original: "{rust}".into(), rust: false, ..Default::default() }); + // error + t(LangString { original: "{rust}".into(), rust: true, ..Default::default() }); t(LangString { original: "{.rust}".into(), rust: false, @@ -107,9 +108,9 @@ fn test_lang_string_parse() { ..Default::default() }); t(LangString { - original: "test_harness,.rust".into(), + original: "test_harness,rusta".into(), test_harness: true, - unknown: vec![".rust".into()], + unknown: vec!["rusta".into()], ..Default::default() }); t(LangString { @@ -194,65 +195,51 @@ fn test_lang_string_parse() { unknown: vec!["unknown".into()], ..Default::default() }); - t(LangString { - original: "{.first.second}".into(), - added_classes: vec!["first.second".into()], - rust: false, - ..Default::default() - }); - t(LangString { - original: "{class=first=second}".into(), - added_classes: vec!["first=second".into()], - rust: false, - ..Default::default() - }); - t(LangString { - original: "{class=first.second}".into(), - added_classes: vec!["first.second".into()], - rust: false, - ..Default::default() - }); - t(LangString { - original: "{class=.first}".into(), - added_classes: vec![".first".into()], - rust: false, - ..Default::default() - }); + // error + t(LangString { original: "{.first.second}".into(), rust: true, ..Default::default() }); + // error + t(LangString { original: "{class=first=second}".into(), rust: true, ..Default::default() }); + // error + t(LangString { original: "{class=first.second}".into(), rust: true, ..Default::default() }); + // error + t(LangString { original: "{class=.first}".into(), rust: true, ..Default::default() }); t(LangString { original: r#"{class="first"}"#.into(), added_classes: vec!["first".into()], rust: false, ..Default::default() }); - t(LangString { - original: r#"{class=f"irst"}"#.into(), - added_classes: vec!["first".into()], - rust: false, - ..Default::default() - }); + // error + t(LangString { original: r#"{class=f"irst"}"#.into(), rust: true, ..Default::default() }); } #[test] fn test_lang_string_tokenizer() { - fn case(lang_string: &str, want: &[TokenKind<'_>]) { + fn case(lang_string: &str, want: &[LangStringToken<'_>]) { let have = TagIterator::new(lang_string, None).collect::>(); assert_eq!(have, want, "Unexpected lang string split for `{}`", lang_string); } case("", &[]); - case("foo", &[TokenKind::Token("foo")]); - case("foo,bar", &[TokenKind::Token("foo"), TokenKind::Token("bar")]); - case(".foo,.bar", &[TokenKind::Token(".foo"), TokenKind::Token(".bar")]); - case("{.foo,.bar}", &[TokenKind::Attribute(".foo"), TokenKind::Attribute(".bar")]); - case(" {.foo,.bar} ", &[TokenKind::Attribute(".foo"), TokenKind::Attribute(".bar")]); - case("foo bar", &[TokenKind::Token("foo"), TokenKind::Token("bar")]); - case("foo\tbar", &[TokenKind::Token("foo"), TokenKind::Token("bar")]); - case("foo\t, bar", &[TokenKind::Token("foo"), TokenKind::Token("bar")]); - case(" foo , bar ", &[TokenKind::Token("foo"), TokenKind::Token("bar")]); - case(",,foo,,bar,,", &[TokenKind::Token("foo"), TokenKind::Token("bar")]); - case("foo=bar", &[TokenKind::Token("foo=bar")]); - case("a-b-c", &[TokenKind::Token("a-b-c")]); - case("a_b_c", &[TokenKind::Token("a_b_c")]); + case("foo", &[LangStringToken::LangToken("foo")]); + case("foo,bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]); + case(".foo,.bar", &[]); + case( + "{.foo,.bar}", + &[LangStringToken::ClassAttribute("foo"), LangStringToken::ClassAttribute("bar")], + ); + case( + " {.foo,.bar} ", + &[LangStringToken::ClassAttribute("foo"), LangStringToken::ClassAttribute("bar")], + ); + case("foo bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]); + case("foo\tbar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]); + case("foo\t, bar", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]); + case(" foo , bar ", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]); + case(",,foo,,bar,,", &[LangStringToken::LangToken("foo"), LangStringToken::LangToken("bar")]); + case("foo=bar", &[]); + case("a-b-c", &[LangStringToken::LangToken("a-b-c")]); + case("a_b_c", &[LangStringToken::LangToken("a_b_c")]); } #[test] diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs index c28921b01f1..dd8759b7e37 100644 --- a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs +++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs @@ -6,14 +6,80 @@ #![feature(no_core)] #![no_core] -/// ```{. class= whatever=hehe #id} } {{ +/// ```{. } /// main; /// ``` -//~^^^ ERROR missing class name after `.` -//~| ERROR missing class name after `class=` -//~| ERROR unsupported attribute `whatever=hehe` -//~| ERROR unsupported attribute `#id` -//~| ERROR unexpected `}` outside attribute block (`{}`) -//~| ERROR unclosed attribute block (`{}`): missing `}` at the end -//~| ERROR unexpected `{` inside attribute block (`{}`) +//~^^^ ERROR unexpected ` ` character after `.` pub fn foo() {} + +/// ```{class= a} +/// main; +/// ``` +//~^^^ ERROR unexpected ` ` character after `=` +pub fn foo2() {} + +/// ```{#id} +/// main; +/// ``` +//~^^^ ERROR unexpected character `#` +pub fn foo3() {} + +/// ```{{ +/// main; +/// ``` +//~^^^ ERROR unexpected character `{` +pub fn foo4() {} + +/// ```} +/// main; +/// ``` +//~^^^ ERROR unexpected character `}` +pub fn foo5() {} + +/// ```) +/// main; +/// ``` +//~^^^ ERROR unexpected character `)` +pub fn foo6() {} + +/// ```{class=} +/// main; +/// ``` +//~^^^ ERROR unexpected `}` character after `=` +pub fn foo7() {} + +/// ```( +/// main; +/// ``` +//~^^^ ERROR unclosed comment: missing `)` at the end +pub fn foo8() {} + +/// ```{class=one=two} +/// main; +/// ``` +//~^^^ ERROR unexpected `=` +pub fn foo9() {} + +/// ```{.one.two} +/// main; +/// ``` +//~^^^ ERROR unexpected `.` character +pub fn foo10() {} + +/// ```{class=.one} +/// main; +/// ``` +//~^^^ ERROR unexpected `.` character after `=` +pub fn foo11() {} + +/// ```{class=one.two} +/// main; +/// ``` +//~^^^ ERROR unexpected `.` character +pub fn foo12() {} + +/// ```{(comment)} +/// main; +/// ``` +//~^^^ ERROR unexpected character `(` +pub fn foo13() {} diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr index f19b62914db..3e0dc4b2f7a 100644 --- a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr +++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr @@ -1,7 +1,7 @@ -error: missing class name after `.` +error: unexpected ` ` character after `.` --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 | -LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | / /// ```{. } LL | | /// main; LL | | /// ``` | |_______^ @@ -13,53 +13,101 @@ LL | #![deny(warnings)] | ^^^^^^^^ = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]` -error: missing class name after `class=` - --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 +error: unexpected ` ` character after `=` + --> $DIR/custom_code_classes_in_docs-warning.rs:15:1 | -LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | / /// ```{class= a} LL | | /// main; LL | | /// ``` | |_______^ -error: unsupported attribute `whatever=hehe` - --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 +error: unexpected character `#` + --> $DIR/custom_code_classes_in_docs-warning.rs:21:1 | -LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | / /// ```{#id} LL | | /// main; LL | | /// ``` | |_______^ -error: unsupported attribute `#id` - --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 +error: unexpected character `{` + --> $DIR/custom_code_classes_in_docs-warning.rs:27:1 | -LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | / /// ```{{ LL | | /// main; LL | | /// ``` | |_______^ -error: unexpected `}` outside attribute block (`{}`) - --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 +error: unexpected character `}` + --> $DIR/custom_code_classes_in_docs-warning.rs:33:1 | -LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | / /// ```} LL | | /// main; LL | | /// ``` | |_______^ -error: unexpected `{` inside attribute block (`{}`) - --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 +error: unexpected character `)` + --> $DIR/custom_code_classes_in_docs-warning.rs:39:1 | -LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | / /// ```) LL | | /// main; LL | | /// ``` | |_______^ -error: unclosed attribute block (`{}`): missing `}` at the end - --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 +error: unexpected `}` character after `=` + --> $DIR/custom_code_classes_in_docs-warning.rs:45:1 | -LL | / /// ```{. class= whatever=hehe #id} } {{ +LL | / /// ```{class=} LL | | /// main; LL | | /// ``` | |_______^ -error: aborting due to 7 previous errors +error: unclosed comment: missing `)` at the end + --> $DIR/custom_code_classes_in_docs-warning.rs:51:1 + | +LL | / /// ```( +LL | | /// main; +LL | | /// ``` + | |_______^ + +error: unexpected `=` character + --> $DIR/custom_code_classes_in_docs-warning.rs:57:1 + | +LL | / /// ```{class=one=two} +LL | | /// main; +LL | | /// ``` + | |_______^ + +error: unexpected `.` character + --> $DIR/custom_code_classes_in_docs-warning.rs:63:1 + | +LL | / /// ```{.one.two} +LL | | /// main; +LL | | /// ``` + | |_______^ + +error: unexpected `.` character after `=` + --> $DIR/custom_code_classes_in_docs-warning.rs:69:1 + | +LL | / /// ```{class=.one} +LL | | /// main; +LL | | /// ``` + | |_______^ + +error: unexpected `.` character + --> $DIR/custom_code_classes_in_docs-warning.rs:75:1 + | +LL | / /// ```{class=one.two} +LL | | /// main; +LL | | /// ``` + | |_______^ + +error: unexpected character `(` + --> $DIR/custom_code_classes_in_docs-warning.rs:81:1 + | +LL | / /// ```{(comment)} +LL | | /// main; +LL | | /// ``` + | |_______^ + +error: aborting due to 13 previous errors diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning2.rs b/tests/rustdoc-ui/custom_code_classes_in_docs-warning2.rs deleted file mode 100644 index b2ce7407ec6..00000000000 --- a/tests/rustdoc-ui/custom_code_classes_in_docs-warning2.rs +++ /dev/null @@ -1,13 +0,0 @@ -// This test ensures that warnings are working as expected for "custom_code_classes_in_docs" -// feature. - -#![feature(custom_code_classes_in_docs)] -#![deny(warnings)] -#![feature(no_core)] -#![no_core] - -/// ```{class=} -/// main; -/// ``` -//~^^^ ERROR missing class name after `class=` -pub fn foo() {} diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning2.stderr b/tests/rustdoc-ui/custom_code_classes_in_docs-warning2.stderr deleted file mode 100644 index 52bb1dae9f6..00000000000 --- a/tests/rustdoc-ui/custom_code_classes_in_docs-warning2.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: missing class name after `class=` - --> $DIR/custom_code_classes_in_docs-warning2.rs:9:1 - | -LL | / /// ```{class=} -LL | | /// main; -LL | | /// ``` - | |_______^ - | -note: the lint level is defined here - --> $DIR/custom_code_classes_in_docs-warning2.rs:5:9 - | -LL | #![deny(warnings)] - | ^^^^^^^^ - = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]` - -error: aborting due to previous error - diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr index 7432af19360..4f2ded78c29 100644 --- a/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr +++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr @@ -1,4 +1,4 @@ -error: unclosed quote string: missing `"` at the end +error: unclosed quote string `"` --> $DIR/custom_code_classes_in_docs-warning3.rs:9:1 | LL | / /// ```{class="} @@ -17,7 +17,7 @@ LL | #![deny(warnings)] | ^^^^^^^^ = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]` -error: unclosed quote string: missing `"` at the end +error: unclosed quote string `"` --> $DIR/custom_code_classes_in_docs-warning3.rs:9:1 | LL | / /// ```{class="} diff --git a/tests/rustdoc/custom_code_classes.rs b/tests/rustdoc/custom_code_classes.rs index f110721c5a7..cd20d8b7d6c 100644 --- a/tests/rustdoc/custom_code_classes.rs +++ b/tests/rustdoc/custom_code_classes.rs @@ -22,7 +22,7 @@ /// /// Testing with multiple "unknown". Only the first should be used. /// -/// ```whatever4{.huhu-c} whatever5 +/// ```whatever4,{.huhu-c} whatever5 /// main; /// ``` pub struct Bar; From 5f3002ebeb4afd1dff8bc2dff24c658ac19ee1fd Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 2 May 2023 22:04:15 +0200 Subject: [PATCH 182/250] Add eBNF and documentation on TagIterator --- src/librustdoc/html/markdown.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 0a741f7815b..482e7b7f260 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -855,6 +855,36 @@ pub(crate) enum Ignore { Some(Vec), } +/// This is the parser for fenced codeblocks attributes. It implements the following eBNF: +/// +/// ```eBNF +/// lang-string = *(token-list / delimited-attribute-list / comment) +/// +/// bareword = CHAR *(CHAR) +/// quoted-string = QUOTE *(NONQUOTE) QUOTE +/// token = bareword / quoted-string +/// sep = COMMA/WS *(COMMA/WS) +/// attribute = (DOT token)/(token EQUAL token) +/// attribute-list = [sep] attribute *(sep attribute) [sep] +/// delimited-attribute-list = OPEN-CURLY-BRACKET attribute-list CLOSE-CURLY-BRACKET +/// token-list = [sep] token *(sep token) [sep] +/// comment = OPEN_PAREN *(all characters) CLOSE_PAREN +/// +/// OPEN_PAREN = "(" +/// CLOSE_PARENT = ")" +/// OPEN-CURLY-BRACKET = "{" +/// CLOSE-CURLY-BRACKET = "}" +/// CHAR = ALPHA / DIGIT / "_" / "-" / ":" +/// QUOTE = %x22 +/// NONQUOTE = %x09 / %x20 / %x21 / %x23-7E ; TAB / SPACE / all printable characters except `"` +/// COMMA = "," +/// DOT = "." +/// EQUAL = "=" +/// +/// ALPHA = %x41-5A / %x61-7A ; A-Z / a-z +/// DIGIT = %x30-39 +/// WS = %x09 / " " +/// ``` pub(crate) struct TagIterator<'a, 'tcx> { inner: Peekable>, data: &'a str, From 6e21e4823d4bc8f8004271e0b327c7ae4ac9103f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 3 May 2023 14:02:36 +0200 Subject: [PATCH 183/250] Fix incorrect codeblock attributes in docs --- compiler/rustc_middle/src/ty/typeck_results.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 7ecc7e6014d..159cbb72a3b 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -165,7 +165,7 @@ pub struct TypeckResults<'tcx> { /// reading places that are mentioned in a closure (because of _ patterns). However, /// to ensure the places are initialized, we introduce fake reads. /// Consider these two examples: - /// ``` (discriminant matching with only wildcard arm) + /// ```ignore (discriminant matching with only wildcard arm) /// let x: u8; /// let c = || match x { _ => () }; /// ``` @@ -173,7 +173,7 @@ pub struct TypeckResults<'tcx> { /// want to capture it. However, we do still want an error here, because `x` should have /// to be initialized at the point where c is created. Therefore, we add a "fake read" /// instead. - /// ``` (destructured assignments) + /// ```ignore (destructured assignments) /// let c = || { /// let (t1, t2) = t; /// } From bbaa930b35667947fb6791527cb8e3a273c8b087 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 3 May 2023 14:44:45 +0200 Subject: [PATCH 184/250] Fix compilation error "the trait bound `SubdiagnosticMessage: From<&std::string::String>` is not satisfied" --- src/librustdoc/passes/check_custom_code_classes.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/passes/check_custom_code_classes.rs b/src/librustdoc/passes/check_custom_code_classes.rs index 246e7f8f331..eb32e796431 100644 --- a/src/librustdoc/passes/check_custom_code_classes.rs +++ b/src/librustdoc/passes/check_custom_code_classes.rs @@ -67,10 +67,11 @@ pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item) .note( // This will list the wrong items to make them more easily searchable. // To ensure the most correct hits, it adds back the 'class:' that was stripped. - &format!( + format!( "found these custom classes: class={}", tests.custom_classes_found.join(",class=") - ), + ) + .as_str(), ) .emit(); } From 87d2aa5fd34ab96f755fe69aed64083e8a246e09 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 4 May 2023 11:17:56 +0200 Subject: [PATCH 185/250] Improve error emitting code --- src/librustdoc/html/markdown.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 482e7b7f260..61dc4357c41 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -26,6 +26,7 @@ //! ``` use rustc_data_structures::fx::FxHashMap; +use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; use rustc_hir::def_id::DefId; use rustc_middle::ty::TyCtxt; pub(crate) use rustc_resolve::rustdoc::main_body_opts; @@ -808,7 +809,7 @@ impl<'tcx> ExtraInfo<'tcx> { ExtraInfo { def_id, sp, tcx } } - fn error_invalid_codeblock_attr(&self, msg: &str) { + fn error_invalid_codeblock_attr(&self, msg: impl Into) { if let Some(def_id) = self.def_id.as_local() { self.tcx.struct_span_lint_hir( crate::lint::INVALID_CODEBLOCK_ATTRIBUTES, @@ -820,7 +821,11 @@ impl<'tcx> ExtraInfo<'tcx> { } } - fn error_invalid_codeblock_attr_with_help(&self, msg: &str, help: &str) { + fn error_invalid_codeblock_attr_with_help( + &self, + msg: impl Into, + help: impl Into, + ) { if let Some(def_id) = self.def_id.as_local() { self.tcx.struct_span_lint_hir( crate::lint::INVALID_CODEBLOCK_ATTRIBUTES, @@ -1246,7 +1251,7 @@ impl LangString { } { if let Some(extra) = extra { extra.error_invalid_codeblock_attr_with_help( - &format!("unknown attribute `{}`. Did you mean `{}`?", x, flag), + format!("unknown attribute `{x}`. Did you mean `{flag}`?"), help, ); } @@ -1263,9 +1268,8 @@ impl LangString { if key == "class" { data.added_classes.push(value.to_owned()); } else if let Some(extra) = extra { - extra.error_invalid_codeblock_attr(&format!( - "unsupported attribute `{key}`" - )); + extra + .error_invalid_codeblock_attr(format!("unsupported attribute `{key}`")); } } LangStringToken::ClassAttribute(class) => { From 113220b970bcecd4463288c459eeeae9ca315bb1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 28 Aug 2023 14:32:01 +0200 Subject: [PATCH 186/250] Update to new `emit_error` API --- src/librustdoc/html/markdown.rs | 16 ++++++++-------- .../passes/check_custom_code_classes.rs | 5 ++--- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 61dc4357c41..db30836b43f 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -921,7 +921,7 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> { Self { inner: data.char_indices().peekable(), data, is_in_attribute_block: false, extra } } - fn emit_error(&self, err: &str) { + fn emit_error(&self, err: impl Into) { if let Some(extra) = self.extra { extra.error_invalid_codeblock_attr(err); } @@ -954,7 +954,7 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> { } else { let class = &self.data[start + 1..pos]; if class.is_empty() { - self.emit_error(&format!("unexpected `{c}` character after `.`")); + self.emit_error(format!("unexpected `{c}` character after `.`")); return None; } else if self.check_after_token() { return Some(LangStringToken::ClassAttribute(class)); @@ -995,7 +995,7 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> { if let Some((_, c)) = self.inner.next() { if c != '=' { - self.emit_error(&format!("expected `=`, found `{}`", c)); + self.emit_error(format!("expected `=`, found `{}`", c)); return None; } } else { @@ -1006,7 +1006,7 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> { Some((pos, '"')) => self.parse_string(pos)?, Some((pos, c)) if is_bareword_char(c) => self.parse_token(pos)?, Some((_, c)) => { - self.emit_error(&format!("unexpected `{c}` character after `=`")); + self.emit_error(format!("unexpected `{c}` character after `=`")); return None; } None => { @@ -1033,7 +1033,7 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> { if c == '}' || is_separator(c) || c == '(' { true } else { - self.emit_error(&format!("unexpected `{c}` character")); + self.emit_error(format!("unexpected `{c}` character")); false } } else { @@ -1052,7 +1052,7 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> { } else if c == '"' || is_bareword_char(c) { return self.parse_key_value(c, pos); } else { - self.emit_error(&format!("unexpected character `{c}`")); + self.emit_error(format!("unexpected character `{c}`")); return None; } } @@ -1080,7 +1080,7 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> { } let indices = self.parse_string(pos)?; if let Some((_, c)) = self.inner.peek().copied() && c != '{' && !is_separator(c) && c != '(' { - self.emit_error(&format!("expected ` `, `{{` or `,` after `\"`, found `{c}`")); + self.emit_error(format!("expected ` `, `{{` or `,` after `\"`, found `{c}`")); return None; } return Some(LangStringToken::LangToken(&self.data[indices.start..indices.end])); @@ -1103,7 +1103,7 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> { } return self.next(); } else { - self.emit_error(&format!("unexpected character `{c}`")); + self.emit_error(format!("unexpected character `{c}`")); return None; } } diff --git a/src/librustdoc/passes/check_custom_code_classes.rs b/src/librustdoc/passes/check_custom_code_classes.rs index eb32e796431..73e80372e4a 100644 --- a/src/librustdoc/passes/check_custom_code_classes.rs +++ b/src/librustdoc/passes/check_custom_code_classes.rs @@ -54,7 +54,7 @@ pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item) let mut tests = TestsWithCustomClasses { custom_classes_found: vec![] }; - let dox = item.attrs.collapsed_doc_value().unwrap_or_default(); + let dox = item.attrs.doc_value(); find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true); if !tests.custom_classes_found.is_empty() && !cx.tcx.features().custom_code_classes_in_docs { @@ -70,8 +70,7 @@ pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item) format!( "found these custom classes: class={}", tests.custom_classes_found.join(",class=") - ) - .as_str(), + ), ) .emit(); } From f038f180fd5c6a70a6018c2609862d5b6912d761 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 28 Aug 2023 15:03:40 +0200 Subject: [PATCH 187/250] Add `custom` tag for markdown codeblocks --- src/librustdoc/html/markdown.rs | 8 +++--- src/librustdoc/html/markdown/tests.rs | 36 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index db30836b43f..177fb1a9426 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -1166,6 +1166,7 @@ impl LangString { let allow_error_code_check = allow_error_code_check.as_bool(); let mut seen_rust_tags = false; let mut seen_other_tags = false; + let mut seen_custom_tag = false; let mut data = LangString::default(); let mut ignores = vec![]; @@ -1195,6 +1196,9 @@ impl LangString { data.rust = true; seen_rust_tags = true; } + LangStringToken::LangToken("custom") => { + seen_custom_tag = true; + } LangStringToken::LangToken("test_harness") => { data.test_harness = true; seen_rust_tags = !seen_other_tags || seen_rust_tags; @@ -1264,7 +1268,6 @@ impl LangString { data.unknown.push(x.to_owned()); } LangStringToken::KeyValueAttribute(key, value) => { - seen_other_tags = true; if key == "class" { data.added_classes.push(value.to_owned()); } else if let Some(extra) = extra { @@ -1273,7 +1276,6 @@ impl LangString { } } LangStringToken::ClassAttribute(class) => { - seen_other_tags = true; data.added_classes.push(class.to_owned()); } } @@ -1284,7 +1286,7 @@ impl LangString { data.ignore = Ignore::Some(ignores); } - data.rust &= !seen_other_tags || seen_rust_tags; + data.rust &= !seen_custom_tag && (!seen_other_tags || seen_rust_tags); data } diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 35b243d3d29..7d89cb0c4e6 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -64,6 +64,12 @@ fn test_lang_string_parse() { t(LangString { original: "{rust}".into(), rust: true, ..Default::default() }); t(LangString { original: "{.rust}".into(), + rust: true, + added_classes: vec!["rust".into()], + ..Default::default() + }); + t(LangString { + original: "custom,{.rust}".into(), rust: false, added_classes: vec!["rust".into()], ..Default::default() @@ -154,12 +160,24 @@ fn test_lang_string_parse() { t(LangString { original: "{class=test}".into(), added_classes: vec!["test".into()], + rust: true, + ..Default::default() + }); + t(LangString { + original: "custom,{class=test}".into(), + added_classes: vec!["test".into()], rust: false, ..Default::default() }); t(LangString { original: "{.test}".into(), added_classes: vec!["test".into()], + rust: true, + ..Default::default() + }); + t(LangString { + original: "custom,{.test}".into(), + added_classes: vec!["test".into()], rust: false, ..Default::default() }); @@ -172,12 +190,24 @@ fn test_lang_string_parse() { t(LangString { original: "{class=test:with:colon .test1}".into(), added_classes: vec!["test:with:colon".into(), "test1".into()], + rust: true, + ..Default::default() + }); + t(LangString { + original: "custom,{class=test:with:colon .test1}".into(), + added_classes: vec!["test:with:colon".into(), "test1".into()], rust: false, ..Default::default() }); t(LangString { original: "{class=first,class=second}".into(), added_classes: vec!["first".into(), "second".into()], + rust: true, + ..Default::default() + }); + t(LangString { + original: "custom,{class=first,class=second}".into(), + added_classes: vec!["first".into(), "second".into()], rust: false, ..Default::default() }); @@ -206,6 +236,12 @@ fn test_lang_string_parse() { t(LangString { original: r#"{class="first"}"#.into(), added_classes: vec!["first".into()], + rust: true, + ..Default::default() + }); + t(LangString { + original: r#"custom,{class="first"}"#.into(), + added_classes: vec!["first".into()], rust: false, ..Default::default() }); From 7c72edf19fa72a1c9596a79ee9221342a27289ad Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 28 Aug 2023 15:07:54 +0200 Subject: [PATCH 188/250] Update documentation for `custom_code_classes_in_docs` feature --- src/doc/rustdoc/src/unstable-features.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index b5d8c819418..0d3f6338af4 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -631,7 +631,7 @@ to jump to a type definition. ```rust #![feature(custom_code_classes_in_docs)] -/// ```{class=language-c} +/// ```custom,{class=language-c} /// int main(void) { return 0; } /// ``` pub struct Bar; @@ -641,12 +641,16 @@ The text `int main(void) { return 0; }` is rendered without highlighting in a co with the class `language-c`. This can be used to highlight other languages through JavaScript libraries for example. +Without the `custom` attribute, it would be generated as a Rust code example with an additional +`language-C` CSS class. Therefore, if you specifically don't want it to be a Rust code example, +don't forget to add the `custom` attribute. + To be noted that you can replace `class=` with `.` to achieve the same result: ```rust #![feature(custom_code_classes_in_docs)] -/// ```{.language-c} +/// ```custom,{.language-c} /// int main(void) { return 0; } /// ``` pub struct Bar; From 71cab6407920ffb12acc3f146c72ba8d135a774e Mon Sep 17 00:00:00 2001 From: Boxy Date: Mon, 23 Jan 2023 16:02:00 +0000 Subject: [PATCH 189/250] special case `TyAndLayout` debug impl --- compiler/rustc_target/src/abi/call/mod.rs | 28 +++++++++++++++++++-- compiler/rustc_target/src/abi/mod.rs | 13 +++++++++- tests/ui/abi/debug.rs | 1 - tests/ui/abi/debug.stderr | 30 +++++++++++------------ 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 1a4a46ceb40..56547bfb426 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -2,6 +2,7 @@ use crate::abi::{self, Abi, Align, FieldsShape, Size}; use crate::abi::{HasDataLayout, TyAbiInterface, TyAndLayout}; use crate::spec::{self, HasTargetSpec}; use rustc_span::Symbol; +use std::fmt; use std::str::FromStr; mod aarch64; @@ -515,12 +516,20 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { /// Information about how to pass an argument to, /// or return a value from, a function, under some ABI. -#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, Hash, HashStable_Generic)] pub struct ArgAbi<'a, Ty> { pub layout: TyAndLayout<'a, Ty>, pub mode: PassMode, } +// Needs to be a custom impl because of the bounds on the `TyAndLayout` debug impl. +impl<'a, Ty: fmt::Display> fmt::Debug for ArgAbi<'a, Ty> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let ArgAbi { layout, mode } = self; + f.debug_struct("ArgAbi").field("layout", layout).field("mode", mode).finish() + } +} + impl<'a, Ty> ArgAbi<'a, Ty> { /// This defines the "default ABI" for that type, that is then later adjusted in `fn_abi_adjust_for_abi`. pub fn new( @@ -694,7 +703,7 @@ impl RiscvInterruptKind { /// /// I will do my best to describe this structure, but these /// comments are reverse-engineered and may be inaccurate. -NDM -#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +#[derive(Clone, PartialEq, Eq, Hash, HashStable_Generic)] pub struct FnAbi<'a, Ty> { /// The LLVM types of each argument. pub args: Box<[ArgAbi<'a, Ty>]>, @@ -715,6 +724,21 @@ pub struct FnAbi<'a, Ty> { pub can_unwind: bool, } +// Needs to be a custom impl because of the bounds on the `TyAndLayout` debug impl. +impl<'a, Ty: fmt::Display> fmt::Debug for FnAbi<'a, Ty> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let FnAbi { args, ret, c_variadic, fixed_count, conv, can_unwind } = self; + f.debug_struct("FnAbi") + .field("args", args) + .field("ret", ret) + .field("c_variadic", c_variadic) + .field("fixed_count", fixed_count) + .field("conv", conv) + .field("can_unwind", can_unwind) + .finish() + } +} + /// Error produced by attempting to adjust a `FnAbi`, for a "foreign" ABI. #[derive(Copy, Clone, Debug, HashStable_Generic)] pub enum AdjustForForeignAbiError { diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index a335585dbf3..636adcf6b17 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -3,6 +3,7 @@ pub use Primitive::*; use crate::json::{Json, ToJson}; +use std::fmt; use std::ops::Deref; use rustc_macros::HashStable_Generic; @@ -24,12 +25,22 @@ impl ToJson for Endian { /// to that obtained from `layout_of(ty)`, as we need to produce /// layouts for which Rust types do not exist, such as enum variants /// or synthetic fields of enums (i.e., discriminants) and fat pointers. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable_Generic)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)] pub struct TyAndLayout<'a, Ty> { pub ty: Ty, pub layout: Layout<'a>, } +impl<'a, Ty: fmt::Display> fmt::Debug for TyAndLayout<'a, Ty> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Print the type in a readable way, not its debug representation. + f.debug_struct("TyAndLayout") + .field("ty", &format_args!("{}", self.ty)) + .field("layout", &self.layout) + .finish() + } +} + impl<'a, Ty> Deref for TyAndLayout<'a, Ty> { type Target = &'a LayoutS; fn deref(&self) -> &&'a LayoutS { diff --git a/tests/ui/abi/debug.rs b/tests/ui/abi/debug.rs index ac37d7b6bff..77715ee4023 100644 --- a/tests/ui/abi/debug.rs +++ b/tests/ui/abi/debug.rs @@ -4,7 +4,6 @@ // normalize-stderr-test "(valid_range): 0\.\.=(4294967295|18446744073709551615)" -> "$1: $$FULL" // This pattern is prepared for when we account for alignment in the niche. // normalize-stderr-test "(valid_range): [1-9]\.\.=(429496729[0-9]|1844674407370955161[0-9])" -> "$1: $$NON_NULL" -// normalize-stderr-test "Leaf\(0x0*20\)" -> "Leaf(0x0...20)" // Some attributes are only computed for release builds: // compile-flags: -O #![feature(rustc_attrs)] diff --git a/tests/ui/abi/debug.stderr b/tests/ui/abi/debug.stderr index f56f5c525ab..ceaf5136a6f 100644 --- a/tests/ui/abi/debug.stderr +++ b/tests/ui/abi/debug.stderr @@ -87,7 +87,7 @@ error: fn_abi_of(test) = FnAbi { conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:16:1 + --> $DIR/debug.rs:15:1 | LL | fn test(_x: u8) -> bool { true } | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:19:1 + --> $DIR/debug.rs:18:1 | LL | type TestFnPtr = fn(bool) -> u8; | ^^^^^^^^^^^^^^ @@ -190,7 +190,7 @@ error: fn_abi_of(test_generic) = FnAbi { args: [ ArgAbi { layout: TyAndLayout { - ty: *const T/#0, + ty: *const T, layout: Layout { size: $SOME_SIZE, align: AbiAndPrefAlign { @@ -257,13 +257,13 @@ error: fn_abi_of(test_generic) = FnAbi { conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:22:1 + --> $DIR/debug.rs:21:1 | LL | fn test_generic(_x: *const T) { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions - --> $DIR/debug.rs:25:1 + --> $DIR/debug.rs:24:1 | LL | const C: () = (); | ^^^^^^^^^^^ @@ -409,7 +409,7 @@ error: ABIs are not compatible conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:41:1 + --> $DIR/debug.rs:40:1 | LL | type TestAbiNe = (fn(u8), fn(u32)); | ^^^^^^^^^^^^^^ @@ -419,7 +419,7 @@ error: ABIs are not compatible args: [ ArgAbi { layout: TyAndLayout { - ty: [u8; Const { ty: usize, kind: Leaf(0x0...20) }], + ty: [u8; 32], layout: Layout { size: Size(32 bytes), align: AbiAndPrefAlign { @@ -490,7 +490,7 @@ error: ABIs are not compatible args: [ ArgAbi { layout: TyAndLayout { - ty: [u32; Const { ty: usize, kind: Leaf(0x0...20) }], + ty: [u32; 32], layout: Layout { size: Size(128 bytes), align: AbiAndPrefAlign { @@ -557,7 +557,7 @@ error: ABIs are not compatible conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:44:1 + --> $DIR/debug.rs:43:1 | LL | type TestAbiNeLarger = (fn([u8; 32]), fn([u32; 32])); | ^^^^^^^^^^^^^^^^^^^^ @@ -700,7 +700,7 @@ error: ABIs are not compatible conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:47:1 + --> $DIR/debug.rs:46:1 | LL | type TestAbiNeFloat = (fn(f32), fn(u32)); | ^^^^^^^^^^^^^^^^^^^ @@ -846,13 +846,13 @@ error: ABIs are not compatible conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:51:1 + --> $DIR/debug.rs:50:1 | LL | type TestAbiNeSign = (fn(i32), fn(u32)); | ^^^^^^^^^^^^^^^^^^ error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/debug.rs:54:46 + --> $DIR/debug.rs:53:46 | LL | type TestAbiEqNonsense = (fn((str, str)), fn((str, str))); | ^^^^^^^^^^ doesn't have a size known at compile-time @@ -861,7 +861,7 @@ LL | type TestAbiEqNonsense = (fn((str, str)), fn((str, str))); = note: only the last element of a tuple may have a dynamically sized type error: `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions - --> $DIR/debug.rs:29:5 + --> $DIR/debug.rs:28:5 | LL | const C: () = (); | ^^^^^^^^^^^ @@ -870,7 +870,7 @@ error: fn_abi_of(assoc_test) = FnAbi { args: [ ArgAbi { layout: TyAndLayout { - ty: &ReErased Adt(S, []), + ty: &S, layout: Layout { size: $SOME_SIZE, align: AbiAndPrefAlign { @@ -949,7 +949,7 @@ error: fn_abi_of(assoc_test) = FnAbi { conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:34:5 + --> $DIR/debug.rs:33:5 | LL | fn assoc_test(&self) { } | ^^^^^^^^^^^^^^^^^^^^ From d79cd171992757f950d74df3d25db47f76818bd1 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 15 Sep 2023 16:40:13 +1000 Subject: [PATCH 190/250] coverage: Arrange imports in `rustc_mir_transform::coverage::debug` --- .../rustc_mir_transform/src/coverage/debug.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs index af616c498fd..cff7fc50324 100644 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ b/compiler/rustc_mir_transform/src/coverage/debug.rs @@ -108,24 +108,23 @@ //! recursively, generating labels with nested operations, enclosed in parentheses //! (for example: `bcb2 + (bcb0 - bcb1)`). -use super::counters::{BcbCounter, CoverageCounters}; -use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; -use super::spans::CoverageSpan; +use std::iter; +use std::ops::Deref; +use std::sync::OnceLock; use itertools::Itertools; +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::mir::coverage::*; use rustc_middle::mir::create_dump_file; use rustc_middle::mir::generic_graphviz::GraphvizWriter; use rustc_middle::mir::spanview::{self, SpanViewable}; - -use rustc_data_structures::fx::FxHashMap; -use rustc_middle::mir::coverage::*; use rustc_middle::mir::{self, BasicBlock}; use rustc_middle::ty::TyCtxt; use rustc_span::Span; -use std::iter; -use std::ops::Deref; -use std::sync::OnceLock; +use super::counters::{BcbCounter, CoverageCounters}; +use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; +use super::spans::CoverageSpan; pub const NESTED_INDENT: &str = " "; From d0d1187ebb548cd3740d96d962ab2fa976fbd711 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 15 Sep 2023 16:38:26 +1000 Subject: [PATCH 191/250] coverage: Update log module names in debug docs --- compiler/rustc_mir_transform/src/coverage/debug.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs index cff7fc50324..6bcb230d0cf 100644 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ b/compiler/rustc_mir_transform/src/coverage/debug.rs @@ -44,7 +44,7 @@ //! points, which can be enabled via environment variable: //! //! ```shell -//! RUSTC_LOG=rustc_mir_transform::transform::coverage=debug +//! RUSTC_LOG=rustc_mir_transform::coverage=debug //! ``` //! //! Other module paths with coverage-related debug logs may also be of interest, particularly for @@ -52,7 +52,7 @@ //! code generation pass). For example: //! //! ```shell -//! RUSTC_LOG=rustc_mir_transform::transform::coverage,rustc_codegen_ssa::coverageinfo,rustc_codegen_llvm::coverageinfo=debug +//! RUSTC_LOG=rustc_mir_transform::coverage,rustc_codegen_llvm::coverageinfo=debug //! ``` //! //! Coverage Debug Options From 3b5b1aa2a51c77430f9db403af38711f0c79ef7b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 15 Sep 2023 16:24:52 +1000 Subject: [PATCH 192/250] coverage: Simplify internal representation of debug types --- .../rustc_mir_transform/src/coverage/debug.rs | 268 +++++++++--------- 1 file changed, 132 insertions(+), 136 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs index 6bcb230d0cf..9b27cff1c8f 100644 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ b/compiler/rustc_mir_transform/src/coverage/debug.rs @@ -258,36 +258,42 @@ impl Default for ExpressionFormat { /// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be /// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`. pub(super) struct DebugCounters { - some_counters: Option>, + state: Option, +} + +#[derive(Default)] +struct DebugCountersState { + counters: FxHashMap, } impl DebugCounters { pub fn new() -> Self { - Self { some_counters: None } + Self { state: None } } pub fn enable(&mut self) { debug_assert!(!self.is_enabled()); - self.some_counters.replace(FxHashMap::default()); + self.state = Some(DebugCountersState::default()); } pub fn is_enabled(&self) -> bool { - self.some_counters.is_some() + self.state.is_some() } pub fn add_counter(&mut self, counter_kind: &BcbCounter, some_block_label: Option) { - if let Some(counters) = &mut self.some_counters { - let id = counter_kind.as_operand(); - counters - .try_insert(id, DebugCounter::new(counter_kind.clone(), some_block_label)) - .expect("attempt to add the same counter_kind to DebugCounters more than once"); - } + let Some(state) = &mut self.state else { return }; + + let id = counter_kind.as_operand(); + state + .counters + .try_insert(id, DebugCounter::new(counter_kind.clone(), some_block_label)) + .expect("attempt to add the same counter_kind to DebugCounters more than once"); } pub fn some_block_label(&self, operand: Operand) -> Option<&String> { - self.some_counters.as_ref().and_then(|counters| { - counters.get(&operand).and_then(|debug_counter| debug_counter.some_block_label.as_ref()) - }) + let Some(state) = &self.state else { return None }; + + state.counters.get(&operand)?.some_block_label.as_ref() } pub fn format_counter(&self, counter_kind: &BcbCounter) -> String { @@ -307,7 +313,7 @@ impl DebugCounters { if counter_format.operation { return format!( "{}{} {} {}", - if counter_format.id || self.some_counters.is_none() { + if counter_format.id || !self.is_enabled() { format!("#{} = ", id.index()) } else { String::new() @@ -323,10 +329,9 @@ impl DebugCounters { } let id = counter_kind.as_operand(); - if self.some_counters.is_some() && (counter_format.block || !counter_format.id) { - let counters = self.some_counters.as_ref().unwrap(); + if let Some(state) = &self.state && (counter_format.block || !counter_format.id) { if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = - counters.get(&id) + state.counters.get(&id) { return if counter_format.id { format!("{}#{:?}", block_label, id) @@ -342,8 +347,10 @@ impl DebugCounters { if matches!(operand, Operand::Zero) { return String::from("0"); } - if let Some(counters) = &self.some_counters { - if let Some(DebugCounter { counter_kind, some_block_label }) = counters.get(&operand) { + if let Some(state) = &self.state { + if let Some(DebugCounter { counter_kind, some_block_label }) = + state.counters.get(&operand) + { if let BcbCounter::Expression { .. } = counter_kind { if let Some(label) = some_block_label && debug_options().counter_format.block { return format!( @@ -377,30 +384,29 @@ impl DebugCounter { /// If enabled, this data structure captures additional debugging information used when generating /// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes. pub(super) struct GraphvizData { - some_bcb_to_coverage_spans_with_counters: - Option>>, - some_bcb_to_dependency_counters: Option>>, - some_edge_to_counter: Option>, + state: Option, +} + +#[derive(Default)] +struct GraphvizDataState { + bcb_to_coverage_spans_with_counters: + FxHashMap>, + bcb_to_dependency_counters: FxHashMap>, + edge_to_counter: FxHashMap<(BasicCoverageBlock, BasicBlock), BcbCounter>, } impl GraphvizData { pub fn new() -> Self { - Self { - some_bcb_to_coverage_spans_with_counters: None, - some_bcb_to_dependency_counters: None, - some_edge_to_counter: None, - } + Self { state: None } } pub fn enable(&mut self) { debug_assert!(!self.is_enabled()); - self.some_bcb_to_coverage_spans_with_counters = Some(FxHashMap::default()); - self.some_bcb_to_dependency_counters = Some(FxHashMap::default()); - self.some_edge_to_counter = Some(FxHashMap::default()); + self.state = Some(GraphvizDataState::default()); } pub fn is_enabled(&self) -> bool { - self.some_bcb_to_coverage_spans_with_counters.is_some() + self.state.is_some() } pub fn add_bcb_coverage_span_with_counter( @@ -409,27 +415,22 @@ impl GraphvizData { coverage_span: &CoverageSpan, counter_kind: &BcbCounter, ) { - if let Some(bcb_to_coverage_spans_with_counters) = - self.some_bcb_to_coverage_spans_with_counters.as_mut() - { - bcb_to_coverage_spans_with_counters - .entry(bcb) - .or_insert_with(Vec::new) - .push((coverage_span.clone(), counter_kind.clone())); - } + let Some(state) = &mut self.state else { return }; + + state + .bcb_to_coverage_spans_with_counters + .entry(bcb) + .or_insert_with(Vec::new) + .push((coverage_span.clone(), counter_kind.clone())); } pub fn get_bcb_coverage_spans_with_counters( &self, bcb: BasicCoverageBlock, ) -> Option<&[(CoverageSpan, BcbCounter)]> { - if let Some(bcb_to_coverage_spans_with_counters) = - self.some_bcb_to_coverage_spans_with_counters.as_ref() - { - bcb_to_coverage_spans_with_counters.get(&bcb).map(Deref::deref) - } else { - None - } + let Some(state) = &self.state else { return None }; + + state.bcb_to_coverage_spans_with_counters.get(&bcb).map(Deref::deref) } pub fn add_bcb_dependency_counter( @@ -437,20 +438,19 @@ impl GraphvizData { bcb: BasicCoverageBlock, counter_kind: &BcbCounter, ) { - if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_mut() { - bcb_to_dependency_counters - .entry(bcb) - .or_insert_with(Vec::new) - .push(counter_kind.clone()); - } + let Some(state) = &mut self.state else { return }; + + state + .bcb_to_dependency_counters + .entry(bcb) + .or_insert_with(Vec::new) + .push(counter_kind.clone()); } pub fn get_bcb_dependency_counters(&self, bcb: BasicCoverageBlock) -> Option<&[BcbCounter]> { - if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_ref() { - bcb_to_dependency_counters.get(&bcb).map(Deref::deref) - } else { - None - } + let Some(state) = &self.state else { return None }; + + state.bcb_to_dependency_counters.get(&bcb).map(Deref::deref) } pub fn set_edge_counter( @@ -459,11 +459,12 @@ impl GraphvizData { to_bb: BasicBlock, counter_kind: &BcbCounter, ) { - if let Some(edge_to_counter) = self.some_edge_to_counter.as_mut() { - edge_to_counter - .try_insert((from_bcb, to_bb), counter_kind.clone()) - .expect("invalid attempt to insert more than one edge counter for the same edge"); - } + let Some(state) = &mut self.state else { return }; + + state + .edge_to_counter + .try_insert((from_bcb, to_bb), counter_kind.clone()) + .expect("invalid attempt to insert more than one edge counter for the same edge"); } pub fn get_edge_counter( @@ -471,11 +472,9 @@ impl GraphvizData { from_bcb: BasicCoverageBlock, to_bb: BasicBlock, ) -> Option<&BcbCounter> { - if let Some(edge_to_counter) = self.some_edge_to_counter.as_ref() { - edge_to_counter.get(&(from_bcb, to_bb)) - } else { - None - } + let Some(state) = &self.state else { return None }; + + state.edge_to_counter.get(&(from_bcb, to_bb)) } } @@ -484,41 +483,42 @@ impl GraphvizData { /// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs /// and/or a `CoverageGraph` graphviz output). pub(super) struct UsedExpressions { - some_used_expression_operands: Option>>, - some_unused_expressions: - Option, BasicCoverageBlock)>>, + state: Option, +} + +#[derive(Default)] +struct UsedExpressionsState { + used_expression_operands: FxHashMap>, + unused_expressions: Vec<(BcbCounter, Option, BasicCoverageBlock)>, } impl UsedExpressions { pub fn new() -> Self { - Self { some_used_expression_operands: None, some_unused_expressions: None } + Self { state: None } } pub fn enable(&mut self) { debug_assert!(!self.is_enabled()); - self.some_used_expression_operands = Some(FxHashMap::default()); - self.some_unused_expressions = Some(Vec::new()); + self.state = Some(UsedExpressionsState::default()) } pub fn is_enabled(&self) -> bool { - self.some_used_expression_operands.is_some() + self.state.is_some() } pub fn add_expression_operands(&mut self, expression: &BcbCounter) { - if let Some(used_expression_operands) = self.some_used_expression_operands.as_mut() { - if let BcbCounter::Expression { id, lhs, rhs, .. } = *expression { - used_expression_operands.entry(lhs).or_insert_with(Vec::new).push(id); - used_expression_operands.entry(rhs).or_insert_with(Vec::new).push(id); - } + let Some(state) = &mut self.state else { return }; + + if let BcbCounter::Expression { id, lhs, rhs, .. } = *expression { + state.used_expression_operands.entry(lhs).or_insert_with(Vec::new).push(id); + state.used_expression_operands.entry(rhs).or_insert_with(Vec::new).push(id); } } pub fn expression_is_used(&self, expression: &BcbCounter) -> bool { - if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() { - used_expression_operands.contains_key(&expression.as_operand()) - } else { - false - } + let Some(state) = &self.state else { return false }; + + state.used_expression_operands.contains_key(&expression.as_operand()) } pub fn add_unused_expression_if_not_found( @@ -527,14 +527,10 @@ impl UsedExpressions { edge_from_bcb: Option, target_bcb: BasicCoverageBlock, ) { - if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() { - if !used_expression_operands.contains_key(&expression.as_operand()) { - self.some_unused_expressions.as_mut().unwrap().push(( - expression.clone(), - edge_from_bcb, - target_bcb, - )); - } + let Some(state) = &mut self.state else { return }; + + if !state.used_expression_operands.contains_key(&expression.as_operand()) { + state.unused_expressions.push((expression.clone(), edge_from_bcb, target_bcb)); } } @@ -543,11 +539,9 @@ impl UsedExpressions { pub fn get_unused_expressions( &self, ) -> Vec<(BcbCounter, Option, BasicCoverageBlock)> { - if let Some(unused_expressions) = self.some_unused_expressions.as_ref() { - unused_expressions.clone() - } else { - Vec::new() - } + let Some(state) = &self.state else { return Vec::new() }; + + state.unused_expressions.clone() } /// If enabled, validate that every BCB or edge counter not directly associated with a coverage @@ -561,51 +555,53 @@ impl UsedExpressions { BcbCounter, )], ) { - if self.is_enabled() { - let mut not_validated = bcb_counters_without_direct_coverage_spans - .iter() - .map(|(_, _, counter_kind)| counter_kind) - .collect::>(); - let mut validating_count = 0; - while not_validated.len() != validating_count { - let to_validate = not_validated.split_off(0); - validating_count = to_validate.len(); - for counter_kind in to_validate { - if self.expression_is_used(counter_kind) { - self.add_expression_operands(counter_kind); - } else { - not_validated.push(counter_kind); - } + if !self.is_enabled() { + return; + } + + let mut not_validated = bcb_counters_without_direct_coverage_spans + .iter() + .map(|(_, _, counter_kind)| counter_kind) + .collect::>(); + let mut validating_count = 0; + while not_validated.len() != validating_count { + let to_validate = not_validated.split_off(0); + validating_count = to_validate.len(); + for counter_kind in to_validate { + if self.expression_is_used(counter_kind) { + self.add_expression_operands(counter_kind); + } else { + not_validated.push(counter_kind); } } } } pub fn alert_on_unused_expressions(&self, debug_counters: &DebugCounters) { - if let Some(unused_expressions) = self.some_unused_expressions.as_ref() { - for (counter_kind, edge_from_bcb, target_bcb) in unused_expressions { - let unused_counter_message = if let Some(from_bcb) = edge_from_bcb.as_ref() { - format!( - "non-coverage edge counter found without a dependent expression, in \ - {:?}->{:?}; counter={}", - from_bcb, - target_bcb, - debug_counters.format_counter(&counter_kind), - ) - } else { - format!( - "non-coverage counter found without a dependent expression, in {:?}; \ - counter={}", - target_bcb, - debug_counters.format_counter(&counter_kind), - ) - }; + let Some(state) = &self.state else { return }; - if debug_options().allow_unused_expressions { - debug!("WARNING: {}", unused_counter_message); - } else { - bug!("{}", unused_counter_message); - } + for (counter_kind, edge_from_bcb, target_bcb) in &state.unused_expressions { + let unused_counter_message = if let Some(from_bcb) = edge_from_bcb.as_ref() { + format!( + "non-coverage edge counter found without a dependent expression, in \ + {:?}->{:?}; counter={}", + from_bcb, + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + } else { + format!( + "non-coverage counter found without a dependent expression, in {:?}; \ + counter={}", + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + }; + + if debug_options().allow_unused_expressions { + debug!("WARNING: {}", unused_counter_message); + } else { + bug!("{}", unused_counter_message); } } } From 91e0b46f76d3e6b144d162f9be9c057b5287304b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 15 Sep 2023 17:18:43 +1000 Subject: [PATCH 193/250] coverage: Replace an unnecessary map with a set This hashmap's values were never used. --- compiler/rustc_mir_transform/src/coverage/debug.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs index 9b27cff1c8f..bb1f16aa8be 100644 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ b/compiler/rustc_mir_transform/src/coverage/debug.rs @@ -113,7 +113,7 @@ use std::ops::Deref; use std::sync::OnceLock; use itertools::Itertools; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_middle::mir::coverage::*; use rustc_middle::mir::create_dump_file; use rustc_middle::mir::generic_graphviz::GraphvizWriter; @@ -488,7 +488,7 @@ pub(super) struct UsedExpressions { #[derive(Default)] struct UsedExpressionsState { - used_expression_operands: FxHashMap>, + used_expression_operands: FxHashSet, unused_expressions: Vec<(BcbCounter, Option, BasicCoverageBlock)>, } @@ -509,16 +509,16 @@ impl UsedExpressions { pub fn add_expression_operands(&mut self, expression: &BcbCounter) { let Some(state) = &mut self.state else { return }; - if let BcbCounter::Expression { id, lhs, rhs, .. } = *expression { - state.used_expression_operands.entry(lhs).or_insert_with(Vec::new).push(id); - state.used_expression_operands.entry(rhs).or_insert_with(Vec::new).push(id); + if let BcbCounter::Expression { lhs, rhs, .. } = *expression { + state.used_expression_operands.insert(lhs); + state.used_expression_operands.insert(rhs); } } pub fn expression_is_used(&self, expression: &BcbCounter) -> bool { let Some(state) = &self.state else { return false }; - state.used_expression_operands.contains_key(&expression.as_operand()) + state.used_expression_operands.contains(&expression.as_operand()) } pub fn add_unused_expression_if_not_found( @@ -529,7 +529,7 @@ impl UsedExpressions { ) { let Some(state) = &mut self.state else { return }; - if !state.used_expression_operands.contains_key(&expression.as_operand()) { + if !state.used_expression_operands.contains(&expression.as_operand()) { state.unused_expressions.push((expression.clone(), edge_from_bcb, target_bcb)); } } From 8184c9c50d227172317debbd9793b26f17ed9fea Mon Sep 17 00:00:00 2001 From: ltdk Date: Sat, 15 Jul 2023 23:17:46 -0400 Subject: [PATCH 194/250] impl Step for IP addresses --- library/core/src/iter/range.rs | 67 +++++++++++++++++++++++++++++++++- tests/ui/range/range-1.stderr | 2 +- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 44feb0a5638..ddd97611b24 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -1,6 +1,7 @@ use crate::ascii::Char as AsciiChar; use crate::convert::TryFrom; use crate::mem; +use crate::net::{Ipv4Addr, Ipv6Addr}; use crate::num::NonZeroUsize; use crate::ops::{self, Try}; @@ -15,7 +16,7 @@ macro_rules! unsafe_impl_trusted_step { unsafe impl TrustedStep for $type {} )*}; } -unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize]; +unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize Ipv4Addr Ipv6Addr]; /// Objects that have a notion of *successor* and *predecessor* operations. /// @@ -527,6 +528,70 @@ impl Step for AsciiChar { } } +#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +impl Step for Ipv4Addr { + #[inline] + fn steps_between(&start: &Ipv4Addr, &end: &Ipv4Addr) -> Option { + u32::steps_between(&start.to_bits(), &end.to_bits()) + } + + #[inline] + fn forward_checked(start: Ipv4Addr, count: usize) -> Option { + u32::forward_checked(start.to_bits(), count).map(Ipv4Addr::from_bits) + } + + #[inline] + fn backward_checked(start: Ipv4Addr, count: usize) -> Option { + u32::backward_checked(start.to_bits(), count).map(Ipv4Addr::from_bits) + } + + #[inline] + unsafe fn forward_unchecked(start: Ipv4Addr, count: usize) -> Ipv4Addr { + // SAFETY: Since u32 and Ipv4Addr are losslessly convertible, + // this is as safe as the u32 version. + Ipv4Addr::from_bits(unsafe { u32::forward_unchecked(start.to_bits(), count) }) + } + + #[inline] + unsafe fn backward_unchecked(start: Ipv4Addr, count: usize) -> Ipv4Addr { + // SAFETY: Since u32 and Ipv4Addr are losslessly convertible, + // this is as safe as the u32 version. + Ipv4Addr::from_bits(unsafe { u32::backward_unchecked(start.to_bits(), count) }) + } +} + +#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +impl Step for Ipv6Addr { + #[inline] + fn steps_between(&start: &Ipv6Addr, &end: &Ipv6Addr) -> Option { + u128::steps_between(&start.to_bits(), &end.to_bits()) + } + + #[inline] + fn forward_checked(start: Ipv6Addr, count: usize) -> Option { + u128::forward_checked(start.to_bits(), count).map(Ipv6Addr::from_bits) + } + + #[inline] + fn backward_checked(start: Ipv6Addr, count: usize) -> Option { + u128::backward_checked(start.to_bits(), count).map(Ipv6Addr::from_bits) + } + + #[inline] + unsafe fn forward_unchecked(start: Ipv6Addr, count: usize) -> Ipv6Addr { + // SAFETY: Since u128 and Ipv6Addr are losslessly convertible, + // this is as safe as the u128 version. + Ipv6Addr::from_bits(unsafe { u128::forward_unchecked(start.to_bits(), count) }) + } + + #[inline] + unsafe fn backward_unchecked(start: Ipv6Addr, count: usize) -> Ipv6Addr { + // SAFETY: Since u128 and Ipv6Addr are losslessly convertible, + // this is as safe as the u128 version. + Ipv6Addr::from_bits(unsafe { u128::backward_unchecked(start.to_bits(), count) }) + } +} + macro_rules! range_exact_iter_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] diff --git a/tests/ui/range/range-1.stderr b/tests/ui/range/range-1.stderr index ecfc56961ee..96c1ffb2f7e 100644 --- a/tests/ui/range/range-1.stderr +++ b/tests/ui/range/range-1.stderr @@ -19,7 +19,7 @@ LL | for i in false..true {} i64 i128 usize - and 6 others + and 8 others = note: required for `std::ops::Range` to implement `Iterator` = note: required for `std::ops::Range` to implement `IntoIterator` From 08aa6c9b650ba83b58600d0794bcf0bc8eef7a42 Mon Sep 17 00:00:00 2001 From: ltdk Date: Fri, 2 Jun 2023 20:07:26 -0400 Subject: [PATCH 195/250] Specialize count for range iterators --- library/core/src/iter/range.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 44feb0a5638..391e03636ab 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -765,6 +765,15 @@ impl Iterator for ops::Range
{ } } + #[inline] + fn count(self) -> usize { + if self.start < self.end { + Step::steps_between(&self.start, &self.end).expect("count overflowed usize") + } else { + 0 + } + } + #[inline] fn nth(&mut self, n: usize) -> Option { self.spec_nth(n) @@ -1162,6 +1171,17 @@ impl Iterator for ops::RangeInclusive { } } + #[inline] + fn count(self) -> usize { + if self.is_empty() { + return 0; + } + + Step::steps_between(&self.start, &self.end) + .and_then(|steps| steps.checked_add(1)) + .expect("count overflowed usize") + } + #[inline] fn nth(&mut self, n: usize) -> Option { if self.is_empty() { From ec1d04967dbe32ded50afaa29209373eff924517 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 Sep 2023 09:44:44 +0200 Subject: [PATCH 196/250] don't globally ignore rustc-ice files --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4c8d1a03764..485968d9c56 100644 --- a/.gitignore +++ b/.gitignore @@ -58,7 +58,6 @@ build/ \#* \#*\# .#* -rustc-ice-*.txt ## Tags tags From e39c39346b8ff10e57fa93563029b4cc39fb2b2a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 16 Sep 2023 11:37:41 +0200 Subject: [PATCH 197/250] Fix invalid markdown codeblock label --- .../rustc_builtin_macros/src/deriving/generic/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 6597ee3cf1b..edc6f9f098e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -88,7 +88,7 @@ //! //! When generating the `expr` for the `A` impl, the `SubstructureFields` is //! -//! ```{.text} +//! ```text //! Struct(vec![FieldInfo { //! span: //! name: Some(), @@ -99,7 +99,7 @@ //! //! For the `B` impl, called with `B(a)` and `B(b)`, //! -//! ```{.text} +//! ```text //! Struct(vec![FieldInfo { //! span: , //! name: None, @@ -113,7 +113,7 @@ //! When generating the `expr` for a call with `self == C0(a)` and `other //! == C0(b)`, the SubstructureFields is //! -//! ```{.text} +//! ```text //! EnumMatching(0, , //! vec![FieldInfo { //! span: @@ -125,7 +125,7 @@ //! //! For `C1 {x}` and `C1 {x}`, //! -//! ```{.text} +//! ```text //! EnumMatching(1, , //! vec![FieldInfo { //! span: @@ -137,7 +137,7 @@ //! //! For the tags, //! -//! ```{.text} +//! ```text //! EnumTag( //! &[, ], ) //! ``` @@ -149,7 +149,7 @@ //! //! A static method on the types above would result in, //! -//! ```{.text} +//! ```text //! StaticStruct(, Named(vec![(, )])) //! //! StaticStruct(, Unnamed(vec![])) From e691752210d18f97d2d4df9c439a3e5b7e2becc5 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 16 Sep 2023 11:54:25 +0200 Subject: [PATCH 198/250] Migrate GUI colors test to original CSS color format --- tests/rustdoc-gui/search-result-color.goml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rustdoc-gui/search-result-color.goml b/tests/rustdoc-gui/search-result-color.goml index f9f81c5ba04..44677dfbfef 100644 --- a/tests/rustdoc-gui/search-result-color.goml +++ b/tests/rustdoc-gui/search-result-color.goml @@ -151,7 +151,7 @@ assert-css: ( ) assert-css: ( "//*[@class='result-name']//*[text()='test_docs::']/ancestor::a", - {"color": "#fff", "background-color": "rgb(60, 60, 60)"}, + {"color": "#fff", "background-color": "#3c3c3c"}, ) // Dark theme From 90348db55001cac995c094287fdeb5ebef86a8f4 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Sun, 3 Sep 2023 18:25:13 +0300 Subject: [PATCH 199/250] create helper module to be embedded for the bootstrap binaries Signed-off-by: onur-ozkan --- src/bootstrap/bin/_helper.rs | 24 ++++++++++++++++++++++++ src/bootstrap/bin/rustc.rs | 17 +++++------------ src/bootstrap/bin/rustdoc.rs | 19 ++++++------------- 3 files changed, 35 insertions(+), 25 deletions(-) create mode 100644 src/bootstrap/bin/_helper.rs diff --git a/src/bootstrap/bin/_helper.rs b/src/bootstrap/bin/_helper.rs new file mode 100644 index 00000000000..09aa471dba4 --- /dev/null +++ b/src/bootstrap/bin/_helper.rs @@ -0,0 +1,24 @@ +/// Parses the value of the "RUSTC_VERBOSE" environment variable and returns it as a `usize`. +/// If it was not defined, returns 0 by default. +/// +/// Panics if "RUSTC_VERBOSE" is defined with the value that is not an unsigned integer. +fn parse_rustc_verbose() -> usize { + use std::str::FromStr; + + match std::env::var("RUSTC_VERBOSE") { + Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"), + Err(_) => 0, + } +} + +/// Parses the value of the "RUSTC_STAGE" environment variable and returns it as a `String`. +/// +/// If "RUSTC_STAGE" was not set, the program will be terminated with 101. +fn parse_rustc_stage() -> String { + std::env::var("RUSTC_STAGE").unwrap_or_else(|_| { + // Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead. + eprintln!("rustc shim: fatal: RUSTC_STAGE was not set"); + eprintln!("rustc shim: note: use `x.py build -vvv` to see all environment variables set by bootstrap"); + exit(101); + }) +} diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 10718aeb89f..20cd63b966b 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -16,27 +16,25 @@ //! never get replaced. include!("../dylib_util.rs"); +include!("./_helper.rs"); use std::env; use std::path::PathBuf; use std::process::{exit, Child, Command}; -use std::str::FromStr; use std::time::Instant; fn main() { let args = env::args_os().skip(1).collect::>(); let arg = |name| args.windows(2).find(|args| args[0] == name).and_then(|args| args[1].to_str()); + let stage = parse_rustc_stage(); + let verbose = parse_rustc_verbose(); + // Detect whether or not we're a build script depending on whether --target // is passed (a bit janky...) let target = arg("--target"); let version = args.iter().find(|w| &**w == "-vV"); - let verbose = match env::var("RUSTC_VERBOSE") { - Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"), - Err(_) => 0, - }; - // Use a different compiler for build scripts, since there may not yet be a // libstd for the real compiler to use. However, if Cargo is attempting to // determine the version of the compiler, the real compiler needs to be @@ -47,12 +45,7 @@ fn main() { } else { ("RUSTC_REAL", "RUSTC_LIBDIR") }; - let stage = env::var("RUSTC_STAGE").unwrap_or_else(|_| { - // Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead. - eprintln!("rustc shim: fatal: RUSTC_STAGE was not set"); - eprintln!("rustc shim: note: use `x.py build -vvv` to see all environment variables set by bootstrap"); - exit(101); - }); + let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set"); let on_fail = env::var_os("RUSTC_ON_FAIL").map(Command::new); diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs index 4ecb3349816..6561c1c1933 100644 --- a/src/bootstrap/bin/rustdoc.rs +++ b/src/bootstrap/bin/rustdoc.rs @@ -9,14 +9,14 @@ use std::process::{exit, Command}; include!("../dylib_util.rs"); +include!("./_helper.rs"); + fn main() { let args = env::args_os().skip(1).collect::>(); - let stage = env::var("RUSTC_STAGE").unwrap_or_else(|_| { - // Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead. - eprintln!("rustc shim: fatal: RUSTC_STAGE was not set"); - eprintln!("rustc shim: note: use `x.py build -vvv` to see all environment variables set by bootstrap"); - exit(101); - }); + + let stage = parse_rustc_stage(); + let verbose = parse_rustc_verbose(); + let rustdoc = env::var_os("RUSTDOC_REAL").expect("RUSTDOC_REAL was not set"); let libdir = env::var_os("RUSTDOC_LIBDIR").expect("RUSTDOC_LIBDIR was not set"); let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set"); @@ -25,13 +25,6 @@ fn main() { // is passed (a bit janky...) let target = args.windows(2).find(|w| &*w[0] == "--target").and_then(|w| w[1].to_str()); - use std::str::FromStr; - - let verbose = match env::var("RUSTC_VERBOSE") { - Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"), - Err(_) => 0, - }; - let mut dylib_path = dylib_path(); dylib_path.insert(0, PathBuf::from(libdir.clone())); From d1ff5e174bf47811916db7a568583e818fb05ae1 Mon Sep 17 00:00:00 2001 From: mxnkarou Date: Sat, 16 Sep 2023 15:46:31 +0200 Subject: [PATCH 200/250] edit `std::vec::Vec::truncate` docs --- library/alloc/src/vec/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 4cfffdf18c6..02331db3341 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1110,8 +1110,8 @@ impl Vec { /// Shortens the vector, keeping the first `len` elements and dropping /// the rest. /// - /// If `len` is greater than the vector's current length, this has no - /// effect. + /// If `len` is greater or equal to the vector's current length, this has + /// no effect. /// /// The [`drain`] method can emulate `truncate`, but causes the excess /// elements to be returned instead of dropped. From 1c7a77a638c523499252feaec986ee99c5d0baf5 Mon Sep 17 00:00:00 2001 From: mxnkarou Date: Sat, 16 Sep 2023 15:52:34 +0200 Subject: [PATCH 201/250] edit `std::collections::VecDeque` docs --- library/alloc/src/collections/vec_deque/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 5965ec2affa..4ef8af9b034 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1015,8 +1015,8 @@ impl VecDeque { /// Shortens the deque, keeping the first `len` elements and dropping /// the rest. /// - /// If `len` is greater than the deque's current length, this has no - /// effect. + /// If `len` is greater or equal to the deque's current length, this has + /// no effect. /// /// # Examples /// From 5a3410ad1a1858489e7b5d8dc3d14837a09fc37f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 Sep 2023 08:36:28 +0200 Subject: [PATCH 202/250] make ty::Const debug printing less verbose --- .../rustc_middle/src/ty/structural_impls.rs | 26 ++++++++++++++----- .../issue_99325.main.built.after.32bit.mir | 4 +-- .../issue_99325.main.built.after.64bit.mir | 4 +-- ...egion_subtyping_basic.main.nll.0.32bit.mir | 2 +- ...egion_subtyping_basic.main.nll.0.64bit.mir | 2 +- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 1b29a83f23e..7c25d0209c9 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -18,6 +18,7 @@ use std::ops::ControlFlow; use std::rc::Rc; use std::sync::Arc; +use super::print::PrettyPrinter; use super::{GenericArg, GenericArgKind, Region}; impl fmt::Debug for ty::TraitDef { @@ -343,14 +344,27 @@ impl<'tcx> DebugWithInfcx> for ty::Const<'tcx> { this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>, f: &mut core::fmt::Formatter<'_>, ) -> core::fmt::Result { - // This reflects what `Const` looked liked before `Interned` was - // introduced. We print it like this to avoid having to update expected - // output in a lot of tests. + // If this is a value, we spend some effort to make it look nice. + if let ConstKind::Value(_) = this.data.kind() { + return ty::tls::with(move |tcx| { + // Somehow trying to lift the valtree results in lifetime errors, so we lift the + // entire constant. + let lifted = tcx.lift(*this.data).unwrap(); + let ConstKind::Value(valtree) = lifted.kind() else { + bug!("we checked that this is a valtree") + }; + let cx = FmtPrinter::new(tcx, Namespace::ValueNS); + let cx = + cx.pretty_print_const_valtree(valtree, lifted.ty(), /*print_ty*/ true)?; + f.write_str(&cx.into_buffer()) + }); + } + // Fall back to something verbose. write!( f, - "Const {{ ty: {:?}, kind: {:?} }}", - &this.map(|data| data.ty()), - &this.map(|data| data.kind()) + "{kind:?}: {ty:?}", + ty = &this.map(|data| data.ty()), + kind = &this.map(|data| data.kind()) ) } } diff --git a/tests/mir-opt/issue_99325.main.built.after.32bit.mir b/tests/mir-opt/issue_99325.main.built.after.32bit.mir index b6a673b6355..132b713356e 100644 --- a/tests/mir-opt/issue_99325.main.built.after.32bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.32bit.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [Const { ty: &ReStatic [u8; Const { ty: usize, kind: Leaf(0x00000004) }], kind: Branch([Leaf(0x41), Leaf(0x41), Leaf(0x41), Leaf(0x41)]) }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:12:16: 12:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [Const { ty: &ReStatic [u8; Const { ty: usize, kind: Leaf(0x00000004) }], kind: UnevaluatedConst { def: DefId(0:8 ~ issue_99325[22bb]::main::{constant#1}), args: [] } }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:12:16: 12:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[22bb]::main::{constant#1}), args: [] }: &ReStatic [u8; 4_usize]], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/issue_99325.main.built.after.64bit.mir b/tests/mir-opt/issue_99325.main.built.after.64bit.mir index 9d112cfa20a..132b713356e 100644 --- a/tests/mir-opt/issue_99325.main.built.after.64bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.64bit.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [Const { ty: &ReStatic [u8; Const { ty: usize, kind: Leaf(0x0000000000000004) }], kind: Branch([Leaf(0x41), Leaf(0x41), Leaf(0x41), Leaf(0x41)]) }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:12:16: 12:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [Const { ty: &ReStatic [u8; Const { ty: usize, kind: Leaf(0x0000000000000004) }], kind: UnevaluatedConst { def: DefId(0:8 ~ issue_99325[22bb]::main::{constant#1}), args: [] } }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:12:16: 12:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[22bb]::main::{constant#1}), args: [] }: &ReStatic [u8; 4_usize]], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir index 56b0f816573..c581d0f8471 100644 --- a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir +++ b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir @@ -22,7 +22,7 @@ | fn main() -> () { let mut _0: (); - let mut _1: [usize; Const { ty: usize, kind: Leaf(0x00000003) }]; + let mut _1: [usize; ValTree(Leaf(0x00000003): usize)]; let _3: usize; let mut _4: usize; let mut _5: bool; diff --git a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir index 83b851eed74..48243e34d08 100644 --- a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir +++ b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir @@ -22,7 +22,7 @@ | fn main() -> () { let mut _0: (); - let mut _1: [usize; Const { ty: usize, kind: Leaf(0x0000000000000003) }]; + let mut _1: [usize; ValTree(Leaf(0x0000000000000003): usize)]; let _3: usize; let mut _4: usize; let mut _5: bool; From 5b882acc2e48e02f11622cf7fb4c02c732a81bfc Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Tue, 12 Sep 2023 19:35:06 -0400 Subject: [PATCH 203/250] Bump to supported Ubuntu The 22.10 Ubuntu repositories were returning 404s in last stable build. --- src/ci/docker/host-x86_64/arm-android/Dockerfile | 2 +- src/ci/docker/host-x86_64/dist-android/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/host-x86_64/arm-android/Dockerfile b/src/ci/docker/host-x86_64/arm-android/Dockerfile index b6b4fdc67a9..db11700af28 100644 --- a/src/ci/docker/host-x86_64/arm-android/Dockerfile +++ b/src/ci/docker/host-x86_64/arm-android/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.10 +FROM ubuntu:23.04 ARG DEBIAN_FRONTEND=noninteractive COPY scripts/android-base-apt-get.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-android/Dockerfile b/src/ci/docker/host-x86_64/dist-android/Dockerfile index 9c6f648896b..b09b6edb01a 100644 --- a/src/ci/docker/host-x86_64/dist-android/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-android/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.10 +FROM ubuntu:23.04 COPY scripts/android-base-apt-get.sh /scripts/ RUN sh /scripts/android-base-apt-get.sh From abd265ed0ed4fff89f87772150da1f66c863d7e1 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Wed, 13 Sep 2023 14:43:08 -0400 Subject: [PATCH 204/250] Move to older, mirrored redox install --- .../docker/host-x86_64/dist-various-1/install-x86_64-redox.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/dist-various-1/install-x86_64-redox.sh b/src/ci/docker/host-x86_64/dist-various-1/install-x86_64-redox.sh index dad97922338..f86402b0180 100755 --- a/src/ci/docker/host-x86_64/dist-various-1/install-x86_64-redox.sh +++ b/src/ci/docker/host-x86_64/dist-various-1/install-x86_64-redox.sh @@ -2,5 +2,5 @@ set -ex -curl https://static.redox-os.org/toolchain/x86_64-unknown-redox/relibc-install.tar.gz | \ +curl https://ci-mirrors.rust-lang.org/rustc/2022-11-27-relibc-install.tar.gz | \ tar --extract --gzip --directory /usr/local From 12b1784b0b317d0ab65b8dbd4da7a6d31a27d558 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Tue, 12 Sep 2023 12:26:05 +0300 Subject: [PATCH 205/250] update the beginning part of `bootstrap/README.md` Signed-off-by: onur-ozkan --- src/bootstrap/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 253d504d7bd..548281ca506 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -1,8 +1,7 @@ # rustbuild - Bootstrapping Rust -This is an in-progress README which is targeted at helping to explain how Rust -is bootstrapped and in general, some of the technical details of the build -system. +This README is aimed at helping to explain how Rust is bootstrapped and in general, +some of the technical details of the build system. Note that this README only covers internal information, not how to use the tool. Please check [bootstrapping dev guide][bootstrapping-dev-guide] for further information. From 9971008b8d83380797df2ab586fcfcfb04f4cfb9 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 14 Sep 2023 21:03:30 +0300 Subject: [PATCH 206/250] micro-level optimizations for bootstrap Overall optimizations for bootstrap on conditions, assertions, trait implementations, etc. Signed-off-by: onur-ozkan --- src/bootstrap/builder.rs | 2 +- src/bootstrap/compile.rs | 6 ++---- src/bootstrap/config.rs | 29 +++++++---------------------- src/bootstrap/config/tests.rs | 14 +++++++------- src/bootstrap/download.rs | 2 +- src/bootstrap/lib.rs | 11 ++++++----- src/bootstrap/llvm.rs | 32 +++++++++++++++++--------------- src/bootstrap/sanity.rs | 21 ++++++++++----------- src/bootstrap/test.rs | 20 +++++++++----------- 9 files changed, 60 insertions(+), 77 deletions(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index b3666192853..7e0f3ccae0b 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -523,7 +523,7 @@ impl<'a> ShouldRun<'a> { .iter() .map(|p| { // assert only if `p` isn't submodule - if !submodules_paths.iter().find(|sm_p| p.contains(*sm_p)).is_some() { + if submodules_paths.iter().find(|sm_p| p.contains(*sm_p)).is_none() { assert!( self.builder.src.join(p).exists(), "`should_run.paths` should correspond to real on-disk paths - use `alias` if there is no relevant path: {}", diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 9c68e5a78d8..2686a8c1752 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -876,10 +876,8 @@ impl Step for Rustc { cargo.rustflag("-Clto=off"); } } - } else { - if builder.config.rust_lto == RustcLto::Off { - cargo.rustflag("-Clto=off"); - } + } else if builder.config.rust_lto == RustcLto::Off { + cargo.rustflag("-Clto=off"); } for krate in &*self.crates { diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index e5fdac3ceda..176ef8e92db 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -322,33 +322,23 @@ pub struct RustfmtMetadata { pub version: String, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub enum RustfmtState { SystemToolchain(PathBuf), Downloaded(PathBuf), Unavailable, + #[default] LazyEvaluated, } -impl Default for RustfmtState { - fn default() -> Self { - RustfmtState::LazyEvaluated - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Default, Clone, Copy, PartialEq)] pub enum LlvmLibunwind { + #[default] No, InTree, System, } -impl Default for LlvmLibunwind { - fn default() -> Self { - Self::No - } -} - impl FromStr for LlvmLibunwind { type Err = String; @@ -362,19 +352,14 @@ impl FromStr for LlvmLibunwind { } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum SplitDebuginfo { Packed, Unpacked, + #[default] Off, } -impl Default for SplitDebuginfo { - fn default() -> Self { - SplitDebuginfo::Off - } -} - impl std::str::FromStr for SplitDebuginfo { type Err = (); @@ -1529,7 +1514,7 @@ impl Config { let asserts = llvm_assertions.unwrap_or(false); config.llvm_from_ci = match llvm.download_ci_llvm { Some(StringOrBool::String(s)) => { - assert!(s == "if-available", "unknown option `{s}` for download-ci-llvm"); + assert_eq!(s, "if-available", "unknown option `{s}` for download-ci-llvm"); crate::llvm::is_ci_llvm_available(&config, asserts) } Some(StringOrBool::Bool(b)) => b, diff --git a/src/bootstrap/config/tests.rs b/src/bootstrap/config/tests.rs index b8f3be96062..aac76cdcbcf 100644 --- a/src/bootstrap/config/tests.rs +++ b/src/bootstrap/config/tests.rs @@ -136,7 +136,7 @@ build-config = {} "setting string value without quotes" ); assert_eq!(config.gdb, Some("bar".into()), "setting string value with quotes"); - assert_eq!(config.deny_warnings, false, "setting boolean value"); + assert!(!config.deny_warnings, "setting boolean value"); assert_eq!( config.tools, Some(["cargo".to_string()].into_iter().collect()), @@ -181,13 +181,13 @@ fn profile_user_dist() { #[test] fn rust_optimize() { - assert_eq!(parse("").rust_optimize.is_release(), true); - assert_eq!(parse("rust.optimize = false").rust_optimize.is_release(), false); - assert_eq!(parse("rust.optimize = true").rust_optimize.is_release(), true); - assert_eq!(parse("rust.optimize = 0").rust_optimize.is_release(), false); - assert_eq!(parse("rust.optimize = 1").rust_optimize.is_release(), true); + assert!(parse("").rust_optimize.is_release()); + assert!(!parse("rust.optimize = false").rust_optimize.is_release()); + assert!(parse("rust.optimize = true").rust_optimize.is_release()); + assert!(!parse("rust.optimize = 0").rust_optimize.is_release()); + assert!(parse("rust.optimize = 1").rust_optimize.is_release()); + assert!(parse("rust.optimize = \"s\"").rust_optimize.is_release()); assert_eq!(parse("rust.optimize = 1").rust_optimize.get_opt_level(), Some("1".to_string())); - assert_eq!(parse("rust.optimize = \"s\"").rust_optimize.is_release(), true); assert_eq!(parse("rust.optimize = \"s\"").rust_optimize.get_opt_level(), Some("s".to_string())); } diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs index 17c308e915b..80845af3b9a 100644 --- a/src/bootstrap/download.rs +++ b/src/bootstrap/download.rs @@ -441,7 +441,7 @@ impl Config { } pub(crate) fn download_beta_toolchain(&self) { - self.verbose(&format!("downloading stage0 beta artifacts")); + self.verbose("downloading stage0 beta artifacts"); let date = &self.stage0_metadata.compiler.date; let version = &self.stage0_metadata.compiler.version; diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 4396bbc51a3..644f61549b8 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -116,7 +116,7 @@ pub const VERSION: usize = 2; /// Extra --check-cfg to add when building /// (Mode restriction, config name, config values (if any)) -const EXTRA_CHECK_CFGS: &[(Option, &'static str, Option<&[&'static str]>)] = &[ +const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ (None, "bootstrap", None), (Some(Mode::Rustc), "parallel_compiler", None), (Some(Mode::ToolRustc), "parallel_compiler", None), @@ -1757,10 +1757,11 @@ to download LLVM rather than building it. // // In these cases we automatically enable Ninja if we find it in the // environment. - if !self.config.ninja_in_file && self.config.build.contains("msvc") { - if cmd_finder.maybe_have("ninja").is_some() { - return true; - } + if !self.config.ninja_in_file + && self.config.build.contains("msvc") + && cmd_finder.maybe_have("ninja").is_some() + { + return true; } self.config.ninja_in_file diff --git a/src/bootstrap/llvm.rs b/src/bootstrap/llvm.rs index c841cb34036..ab2967bb8cc 100644 --- a/src/bootstrap/llvm.rs +++ b/src/bootstrap/llvm.rs @@ -148,7 +148,7 @@ pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String { "".to_owned() }; - if &llvm_sha == "" { + if llvm_sha.is_empty() { eprintln!("error: could not find commit hash for downloading LLVM"); eprintln!("help: maybe your repository history is too shallow?"); eprintln!("help: consider disabling `download-ci-llvm`"); @@ -201,10 +201,10 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool { ("x86_64-unknown-netbsd", false), ]; - if !supported_platforms.contains(&(&*config.build.triple, asserts)) { - if asserts == true || !supported_platforms.contains(&(&*config.build.triple, true)) { - return false; - } + if !supported_platforms.contains(&(&*config.build.triple, asserts)) + && (asserts || !supported_platforms.contains(&(&*config.build.triple, true))) + { + return false; } if is_ci_llvm_modified(config) { @@ -490,11 +490,11 @@ impl Step for Llvm { let mut cmd = Command::new(&res.llvm_config); let version = output(cmd.arg("--version")); let major = version.split('.').next().unwrap(); - let lib_name = match &llvm_version_suffix { + + match &llvm_version_suffix { Some(version_suffix) => format!("libLLVM-{major}{version_suffix}.{extension}"), None => format!("libLLVM-{major}.{extension}"), - }; - lib_name + } }; // When building LLVM with LLVM_LINK_LLVM_DYLIB for macOS, an unversioned @@ -749,13 +749,15 @@ fn configure_cmake( // For distribution we want the LLVM tools to be *statically* linked to libstdc++. // We also do this if the user explicitly requested static libstdc++. - if builder.config.llvm_static_stdcpp { - if !target.contains("msvc") && !target.contains("netbsd") && !target.contains("solaris") { - if target.contains("apple") || target.contains("windows") { - ldflags.push_all("-static-libstdc++"); - } else { - ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++"); - } + if builder.config.llvm_static_stdcpp + && !target.contains("msvc") + && !target.contains("netbsd") + && !target.contains("solaris") + { + if target.contains("apple") || target.contains("windows") { + ldflags.push_all("-static-libstdc++"); + } else { + ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++"); } } diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index 7e83b508e94..0e1c855ea3e 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -92,20 +92,19 @@ pub fn check(build: &mut Build) { .unwrap_or(true) }) .any(|build_llvm_ourselves| build_llvm_ourselves); + let need_cmake = building_llvm || build.config.any_sanitizers_enabled(); - if need_cmake { - if cmd_finder.maybe_have("cmake").is_none() { - eprintln!( - " + if need_cmake && cmd_finder.maybe_have("cmake").is_none() { + eprintln!( + " Couldn't find required command: cmake You should install cmake, or set `download-ci-llvm = true` in the `[llvm]` section of `config.toml` to download LLVM rather than building it. " - ); - crate::exit!(1); - } + ); + crate::exit!(1); } build.config.python = build @@ -199,10 +198,10 @@ than building it. .entry(*target) .or_insert_with(|| Target::from_triple(&target.triple)); - if target.contains("-none-") || target.contains("nvptx") { - if build.no_std(*target) == Some(false) { - panic!("All the *-none-* and nvptx* targets are no-std targets") - } + if (target.contains("-none-") || target.contains("nvptx")) + && build.no_std(*target) == Some(false) + { + panic!("All the *-none-* and nvptx* targets are no-std targets") } // Make sure musl-root is valid diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index d1018978f78..e3a0580b2cc 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1139,16 +1139,14 @@ help: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to .map(|filename| builder.src.join("src/etc/completions").join(filename)); if builder.config.cmd.bless() { builder.ensure(crate::run::GenerateCompletions); - } else { - if crate::flags::get_completion(shells::Bash, &bash).is_some() - || crate::flags::get_completion(shells::Fish, &fish).is_some() - || crate::flags::get_completion(shells::PowerShell, &powershell).is_some() - { - eprintln!( - "x.py completions were changed; run `x.py run generate-completions` to update them" - ); - crate::exit!(1); - } + } else if crate::flags::get_completion(shells::Bash, &bash).is_some() + || crate::flags::get_completion(shells::Fish, &fish).is_some() + || crate::flags::get_completion(shells::PowerShell, &powershell).is_some() + { + eprintln!( + "x.py completions were changed; run `x.py run generate-completions` to update them" + ); + crate::exit!(1); } } @@ -1372,7 +1370,7 @@ impl Step for MirOpt { let run = |target| { builder.ensure(Compiletest { compiler: self.compiler, - target: target, + target, mode: "mir-opt", suite: "mir-opt", path: "tests/mir-opt", From a77789e7aa50a652cbd15cfd400acdfc4602167c Mon Sep 17 00:00:00 2001 From: jDomantas Date: Sun, 17 Sep 2023 11:54:42 +0300 Subject: [PATCH 207/250] regression test --- .../src/handlers/mutability_errors.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 8976a3de67b..d03a0009c48 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -1170,6 +1170,27 @@ fn f() { loop {} for _ in 0..2 {} } +"#, + ); + } + + #[test] + fn regression_15623() { + check_diagnostics( + r#" +struct Foo; + +impl Foo { + fn needs_mut(&mut self) {} +} + +fn main() { + let mut foo = Foo; + || { + let 0 = 1 else { return }; + foo.needs_mut(); + }; +} "#, ); } From 5d18a0edb440c1b1bcd581495f71c894bdb2adfa Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 17 Sep 2023 08:54:56 +0000 Subject: [PATCH 208/250] Do not clone MIR for const-prop lint. --- .../src/const_prop_lint.rs | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index b52827a1e88..fb33b3b49d3 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -105,25 +105,12 @@ impl<'tcx> MirLint<'tcx> for ConstProp { trace!("ConstProp starting for {:?}", def_id); - let dummy_body = &Body::new( - body.source, - (*body.basic_blocks).to_owned(), - body.source_scopes.clone(), - body.local_decls.clone(), - Default::default(), - body.arg_count, - Default::default(), - body.span, - body.generator_kind(), - body.tainted_by_errors, - ); - // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold // constants, instead of just checking for const-folding succeeding. // That would require a uniform one-def no-mutation analysis // and RPO (or recursing when needing the value of a local). - let mut optimization_finder = ConstPropagator::new(body, dummy_body, tcx); - optimization_finder.visit_body(body); + let mut linter = ConstPropagator::new(body, tcx); + linter.visit_body(body); trace!("ConstProp done for {:?}", def_id); } @@ -169,11 +156,7 @@ impl<'tcx> ty::layout::HasParamEnv<'tcx> for ConstPropagator<'_, 'tcx> { } impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { - fn new( - body: &Body<'tcx>, - dummy_body: &'mir Body<'tcx>, - tcx: TyCtxt<'tcx>, - ) -> ConstPropagator<'mir, 'tcx> { + fn new(body: &'mir Body<'tcx>, tcx: TyCtxt<'tcx>) -> ConstPropagator<'mir, 'tcx> { let def_id = body.source.def_id(); let args = &GenericArgs::identity_for_item(tcx, def_id); let param_env = tcx.param_env_reveal_all_normalized(def_id); @@ -204,7 +187,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ecx.push_stack_frame( Instance::new(def_id, args), - dummy_body, + body, &ret, StackPopCleanup::Root { cleanup: false }, ) From a0c31b73d8b81090e12605ca0392f3d4f677f55a Mon Sep 17 00:00:00 2001 From: jDomantas Date: Sun, 17 Sep 2023 12:26:22 +0300 Subject: [PATCH 209/250] don't skip the rest of the block after let-else --- crates/hir-ty/src/infer/closure.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 1f040393f1f..13d6b5643ac 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -469,13 +469,13 @@ impl InferenceContext<'_> { Statement::Let { pat, type_ref: _, initializer, else_branch } => { if let Some(else_branch) = else_branch { self.consume_expr(*else_branch); - if let Some(initializer) = initializer { - self.consume_expr(*initializer); - } - return; } if let Some(initializer) = initializer { - self.walk_expr(*initializer); + if else_branch.is_some() { + self.consume_expr(*initializer); + } else { + self.walk_expr(*initializer); + } if let Some(place) = self.place_of_expr(*initializer) { self.consume_with_pat(place, *pat); } From e1294b26af2d7f731381ef18623f78f39a67f5d7 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 17 Sep 2023 14:52:45 +0200 Subject: [PATCH 210/250] Don't emit an error if the `custom_code_classes_in_docs` feature is disabled when its syntax is used. --- src/librustdoc/doctest.rs | 1 + src/librustdoc/externalfiles.rs | 4 + src/librustdoc/html/markdown.rs | 95 +++++++++++++++---- src/librustdoc/html/render/mod.rs | 9 +- src/librustdoc/markdown.rs | 14 ++- .../passes/calculate_doc_coverage.rs | 9 +- .../passes/check_custom_code_classes.rs | 31 ++++-- .../passes/check_doc_test_visibility.rs | 9 +- .../passes/lint/check_code_block_syntax.rs | 4 +- 9 files changed, 144 insertions(+), 32 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index ea87268877f..24597c3aca3 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1243,6 +1243,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { def_id.to_def_id(), span_of_fragments(&attrs.doc_strings).unwrap_or(sp), )), + self.tcx.features().custom_code_classes_in_docs, ); } diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs index f0ebb8e5a39..b34b69b1f15 100644 --- a/src/librustdoc/externalfiles.rs +++ b/src/librustdoc/externalfiles.rs @@ -46,6 +46,8 @@ impl ExternalHtml { edition, playground, heading_offset: HeadingOffset::H2, + // For external files, it'll be disabled until the feature is enabled by default. + custom_code_classes_in_docs: false, } .into_string() ); @@ -61,6 +63,8 @@ impl ExternalHtml { edition, playground, heading_offset: HeadingOffset::H2, + // For external files, it'll be disabled until the feature is enabled by default. + custom_code_classes_in_docs: false, } .into_string() ); diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 177fb1a9426..59958fbaef9 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -20,6 +20,7 @@ //! edition: Edition::Edition2015, //! playground: &None, //! heading_offset: HeadingOffset::H2, +//! custom_code_classes_in_docs: true, //! }; //! let html = md.into_string(); //! // ... something using html @@ -95,6 +96,8 @@ pub struct Markdown<'a> { /// Offset at which we render headings. /// E.g. if `heading_offset: HeadingOffset::H2`, then `# something` renders an `

`. pub heading_offset: HeadingOffset, + /// `true` if the `custom_code_classes_in_docs` feature is enabled. + pub custom_code_classes_in_docs: bool, } /// A struct like `Markdown` that renders the markdown with a table of contents. pub(crate) struct MarkdownWithToc<'a> { @@ -103,6 +106,8 @@ pub(crate) struct MarkdownWithToc<'a> { pub(crate) error_codes: ErrorCodes, pub(crate) edition: Edition, pub(crate) playground: &'a Option, + /// `true` if the `custom_code_classes_in_docs` feature is enabled. + pub(crate) custom_code_classes_in_docs: bool, } /// A tuple struct like `Markdown` that renders the markdown escaping HTML tags /// and includes no paragraph tags. @@ -203,6 +208,7 @@ struct CodeBlocks<'p, 'a, I: Iterator>> { // Information about the playground if a URL has been specified, containing an // optional crate name and the URL. playground: &'p Option, + custom_code_classes_in_docs: bool, } impl<'p, 'a, I: Iterator>> CodeBlocks<'p, 'a, I> { @@ -211,8 +217,15 @@ impl<'p, 'a, I: Iterator>> CodeBlocks<'p, 'a, I> { error_codes: ErrorCodes, edition: Edition, playground: &'p Option, + custom_code_classes_in_docs: bool, ) -> Self { - CodeBlocks { inner: iter, check_error_codes: error_codes, edition, playground } + CodeBlocks { + inner: iter, + check_error_codes: error_codes, + edition, + playground, + custom_code_classes_in_docs, + } } } @@ -242,8 +255,12 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { let parse_result = match kind { CodeBlockKind::Fenced(ref lang) => { - let parse_result = - LangString::parse_without_check(lang, self.check_error_codes, false); + let parse_result = LangString::parse_without_check( + lang, + self.check_error_codes, + false, + self.custom_code_classes_in_docs, + ); if !parse_result.rust { let added_classes = parse_result.added_classes; let lang_string = if let Some(lang) = parse_result.unknown.first() { @@ -725,8 +742,17 @@ pub(crate) fn find_testable_code( error_codes: ErrorCodes, enable_per_target_ignores: bool, extra_info: Option<&ExtraInfo<'_>>, + custom_code_classes_in_docs: bool, ) { - find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false) + find_codes( + doc, + tests, + error_codes, + enable_per_target_ignores, + extra_info, + false, + custom_code_classes_in_docs, + ) } pub(crate) fn find_codes( @@ -736,6 +762,7 @@ pub(crate) fn find_codes( enable_per_target_ignores: bool, extra_info: Option<&ExtraInfo<'_>>, include_non_rust: bool, + custom_code_classes_in_docs: bool, ) { let mut parser = Parser::new(doc).into_offset_iter(); let mut prev_offset = 0; @@ -754,6 +781,7 @@ pub(crate) fn find_codes( error_codes, enable_per_target_ignores, extra_info, + custom_code_classes_in_docs, ) } } @@ -1153,8 +1181,15 @@ impl LangString { string: &str, allow_error_code_check: ErrorCodes, enable_per_target_ignores: bool, + custom_code_classes_in_docs: bool, ) -> Self { - Self::parse(string, allow_error_code_check, enable_per_target_ignores, None) + Self::parse( + string, + allow_error_code_check, + enable_per_target_ignores, + None, + custom_code_classes_in_docs, + ) } fn parse( @@ -1162,6 +1197,7 @@ impl LangString { allow_error_code_check: ErrorCodes, enable_per_target_ignores: bool, extra: Option<&ExtraInfo<'_>>, + custom_code_classes_in_docs: bool, ) -> Self { let allow_error_code_check = allow_error_code_check.as_bool(); let mut seen_rust_tags = false; @@ -1197,7 +1233,11 @@ impl LangString { seen_rust_tags = true; } LangStringToken::LangToken("custom") => { - seen_custom_tag = true; + if custom_code_classes_in_docs { + seen_custom_tag = true; + } else { + seen_other_tags = true; + } } LangStringToken::LangToken("test_harness") => { data.test_harness = true; @@ -1268,11 +1308,16 @@ impl LangString { data.unknown.push(x.to_owned()); } LangStringToken::KeyValueAttribute(key, value) => { - if key == "class" { - data.added_classes.push(value.to_owned()); - } else if let Some(extra) = extra { - extra - .error_invalid_codeblock_attr(format!("unsupported attribute `{key}`")); + if custom_code_classes_in_docs { + if key == "class" { + data.added_classes.push(value.to_owned()); + } else if let Some(extra) = extra { + extra.error_invalid_codeblock_attr(format!( + "unsupported attribute `{key}`" + )); + } + } else { + seen_other_tags = true; } } LangStringToken::ClassAttribute(class) => { @@ -1302,6 +1347,7 @@ impl Markdown<'_> { edition, playground, heading_offset, + custom_code_classes_in_docs, } = self; // This is actually common enough to special-case @@ -1324,7 +1370,7 @@ impl Markdown<'_> { let p = Footnotes::new(p); let p = LinkReplacer::new(p.map(|(ev, _)| ev), links); let p = TableWrapper::new(p); - let p = CodeBlocks::new(p, codes, edition, playground); + let p = CodeBlocks::new(p, codes, edition, playground, custom_code_classes_in_docs); html::push_html(&mut s, p); s @@ -1333,7 +1379,14 @@ impl Markdown<'_> { impl MarkdownWithToc<'_> { pub(crate) fn into_string(self) -> String { - let MarkdownWithToc { content: md, ids, error_codes: codes, edition, playground } = self; + let MarkdownWithToc { + content: md, + ids, + error_codes: codes, + edition, + playground, + custom_code_classes_in_docs, + } = self; let p = Parser::new_ext(md, main_body_opts()).into_offset_iter(); @@ -1345,7 +1398,7 @@ impl MarkdownWithToc<'_> { let p = HeadingLinks::new(p, Some(&mut toc), ids, HeadingOffset::H1); let p = Footnotes::new(p); let p = TableWrapper::new(p.map(|(ev, _)| ev)); - let p = CodeBlocks::new(p, codes, edition, playground); + let p = CodeBlocks::new(p, codes, edition, playground, custom_code_classes_in_docs); html::push_html(&mut s, p); } @@ -1786,7 +1839,11 @@ pub(crate) struct RustCodeBlock { /// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or /// untagged (and assumed to be rust). -pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec { +pub(crate) fn rust_code_blocks( + md: &str, + extra_info: &ExtraInfo<'_>, + custom_code_classes_in_docs: bool, +) -> Vec { let mut code_blocks = vec![]; if md.is_empty() { @@ -1803,7 +1860,13 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec) -> String { error_codes: shared.codes, edition: shared.edition(), playground: &shared.playground, - heading_offset: HeadingOffset::H1 + heading_offset: HeadingOffset::H1, + custom_code_classes_in_docs: false, } .into_string() ) @@ -437,6 +438,7 @@ fn render_markdown<'a, 'cx: 'a>( heading_offset: HeadingOffset, ) -> impl fmt::Display + 'a + Captures<'cx> { display_fn(move |f| { + let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs; write!( f, "
{}
", @@ -448,6 +450,7 @@ fn render_markdown<'a, 'cx: 'a>( edition: cx.shared.edition(), playground: &cx.shared.playground, heading_offset, + custom_code_classes_in_docs, } .into_string() ) @@ -1778,6 +1781,7 @@ fn render_impl(

", ); } + let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs; write!( w, "
{}
", @@ -1788,7 +1792,8 @@ fn render_impl( error_codes: cx.shared.codes, edition: cx.shared.edition(), playground: &cx.shared.playground, - heading_offset: HeadingOffset::H4 + heading_offset: HeadingOffset::H4, + custom_code_classes_in_docs, } .into_string() ); diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 526eea30478..1ac3163b2cd 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -80,6 +80,8 @@ pub(crate) fn render>( error_codes, edition, playground: &playground, + // For markdown files, it'll be disabled until the feature is enabled by default. + custom_code_classes_in_docs: false, } .into_string() } else { @@ -91,6 +93,8 @@ pub(crate) fn render>( edition, playground: &playground, heading_offset: HeadingOffset::H1, + // For markdown files, it'll be disabled until the feature is enabled by default. + custom_code_classes_in_docs: false, } .into_string() }; @@ -154,7 +158,15 @@ pub(crate) fn test(options: Options) -> Result<(), String> { collector.set_position(DUMMY_SP); let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); - find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None); + // For markdown files, it'll be disabled until the feature is enabled by default. + find_testable_code( + &input_str, + &mut collector, + codes, + options.enable_per_target_ignores, + None, + false, + ); crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests); Ok(()) diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 592dd0a145c..60def40588a 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -208,7 +208,14 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> { let has_docs = !i.attrs.doc_strings.is_empty(); let mut tests = Tests { found_tests: 0 }; - find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, false, None); + find_testable_code( + &i.doc_value(), + &mut tests, + ErrorCodes::No, + false, + None, + self.ctx.tcx.features().custom_code_classes_in_docs, + ); let has_doc_example = tests.found_tests != 0; let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap(); diff --git a/src/librustdoc/passes/check_custom_code_classes.rs b/src/librustdoc/passes/check_custom_code_classes.rs index 73e80372e4a..1a703a4e967 100644 --- a/src/librustdoc/passes/check_custom_code_classes.rs +++ b/src/librustdoc/passes/check_custom_code_classes.rs @@ -9,7 +9,9 @@ use crate::core::DocContext; use crate::fold::DocFolder; use crate::html::markdown::{find_codes, ErrorCodes, LangString}; -use rustc_session::parse::feature_err; +use rustc_errors::StashKey; +use rustc_feature::GateIssue; +use rustc_session::parse::add_feature_diagnostics_for_issue; use rustc_span::symbol::sym; pub(crate) const CHECK_CUSTOM_CODE_CLASSES: Pass = Pass { @@ -55,23 +57,32 @@ pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item) let mut tests = TestsWithCustomClasses { custom_classes_found: vec![] }; let dox = item.attrs.doc_value(); - find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true); + find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true, true); if !tests.custom_classes_found.is_empty() && !cx.tcx.features().custom_code_classes_in_docs { - feature_err( - &cx.tcx.sess.parse_sess, + let span = item.attr_span(cx.tcx); + let sess = &cx.tcx.sess.parse_sess; + let mut err = sess + .span_diagnostic + .struct_span_warn(span, "custom classes in code blocks will change behaviour"); + add_feature_diagnostics_for_issue( + &mut err, + sess, sym::custom_code_classes_in_docs, - item.attr_span(cx.tcx), - "custom classes in code blocks are unstable", - ) - .note( + GateIssue::Language, + false, + ); + + err.note( // This will list the wrong items to make them more easily searchable. // To ensure the most correct hits, it adds back the 'class:' that was stripped. format!( "found these custom classes: class={}", tests.custom_classes_found.join(",class=") ), - ) - .emit(); + ); + + // A later feature_err call can steal and cancel this warning. + err.stash(span, StashKey::EarlySyntaxWarning); } } diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 96224d7c6e2..d1c4cc1f595 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -113,7 +113,14 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item let mut tests = Tests { found_tests: 0 }; - find_testable_code(dox, &mut tests, ErrorCodes::No, false, None); + find_testable_code( + dox, + &mut tests, + ErrorCodes::No, + false, + None, + cx.tcx.features().custom_code_classes_in_docs, + ); if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples { if should_have_doc_example(cx, item) { diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs index 316b1a41c7d..ac8a75a4f18 100644 --- a/src/librustdoc/passes/lint/check_code_block_syntax.rs +++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs @@ -20,7 +20,9 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) { if let Some(dox) = &item.opt_doc_value() { let sp = item.attr_span(cx.tcx); let extra = crate::html::markdown::ExtraInfo::new(cx.tcx, item.item_id.expect_def_id(), sp); - for code_block in markdown::rust_code_blocks(dox, &extra) { + for code_block in + markdown::rust_code_blocks(dox, &extra, cx.tcx.features().custom_code_classes_in_docs) + { check_rust_syntax(cx, item, dox, code_block); } } From c17abf124c5473d6cb24b3d1cc462bb3c71b55ae Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 17 Sep 2023 14:53:30 +0200 Subject: [PATCH 211/250] Update tests for `custom_code_classes_in_docs` feature --- src/librustdoc/html/markdown/tests.rs | 7 +++++-- .../rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs | 7 ++++++- .../feature-gate-custom_code_classes_in_docs.stderr | 7 +++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 7d89cb0c4e6..32957ac57fa 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -49,7 +49,7 @@ fn test_unique_id() { fn test_lang_string_parse() { fn t(lg: LangString) { let s = &lg.original; - assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None), lg) + assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None, true), lg) } t(Default::default()); @@ -290,6 +290,7 @@ fn test_header() { edition: DEFAULT_EDITION, playground: &None, heading_offset: HeadingOffset::H2, + custom_code_classes_in_docs: true, } .into_string(); assert_eq!(output, expect, "original: {}", input); @@ -329,6 +330,7 @@ fn test_header_ids_multiple_blocks() { edition: DEFAULT_EDITION, playground: &None, heading_offset: HeadingOffset::H2, + custom_code_classes_in_docs: true, } .into_string(); assert_eq!(output, expect, "original: {}", input); @@ -433,7 +435,7 @@ fn test_find_testable_code_line() { } } let mut lines = Vec::::new(); - find_testable_code(input, &mut lines, ErrorCodes::No, false, None); + find_testable_code(input, &mut lines, ErrorCodes::No, false, None, true); assert_eq!(lines, expect); } @@ -458,6 +460,7 @@ fn test_ascii_with_prepending_hashtag() { edition: DEFAULT_EDITION, playground: &None, heading_offset: HeadingOffset::H2, + custom_code_classes_in_docs: true, } .into_string(); assert_eq!(output, expect, "original: {}", input); diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs index 8aa13b2d5d1..3f0f8b5b9f9 100644 --- a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs +++ b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs @@ -1,5 +1,10 @@ +// check-pass + /// ```{class=language-c} /// int main(void) { return 0; } /// ``` -//~^^^ ERROR 1:1: 3:8: custom classes in code blocks are unstable [E0658] +//~^^^ WARNING custom classes in code blocks will change behaviour +//~| NOTE found these custom classes: class=language-c +//~| NOTE see issue #79483 +//~| HELP add `#![feature(custom_code_classes_in_docs)]` to the crate attributes to enable pub struct Bar; diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr index c41ebfc8073..1a2360d9b30 100644 --- a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr +++ b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr @@ -1,5 +1,5 @@ -error[E0658]: custom classes in code blocks are unstable - --> $DIR/feature-gate-custom_code_classes_in_docs.rs:1:1 +warning: custom classes in code blocks will change behaviour + --> $DIR/feature-gate-custom_code_classes_in_docs.rs:3:1 | LL | / /// ```{class=language-c} LL | | /// int main(void) { return 0; } @@ -10,6 +10,5 @@ LL | | /// ``` = help: add `#![feature(custom_code_classes_in_docs)]` to the crate attributes to enable = note: found these custom classes: class=language-c -error: aborting due to previous error +warning: 1 warning emitted -For more information about this error, try `rustc --explain E0658`. From b3aba94cbdde83c66d23e5560840b247250aa065 Mon Sep 17 00:00:00 2001 From: jDomantas Date: Sun, 17 Sep 2023 16:52:32 +0300 Subject: [PATCH 212/250] use code from bug report for regression test --- crates/ide-diagnostics/src/handlers/mutability_errors.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index d03a0009c48..d056e5c85cc 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -1178,18 +1178,20 @@ fn f() { fn regression_15623() { check_diagnostics( r#" +//- minicore: fn + struct Foo; impl Foo { fn needs_mut(&mut self) {} } -fn main() { - let mut foo = Foo; - || { +fn foo(mut foo: Foo) { + let mut call_me = || { let 0 = 1 else { return }; foo.needs_mut(); }; + call_me(); } "#, ); From a961068504de97fca810d766d72a6ee913899a6e Mon Sep 17 00:00:00 2001 From: jDomantas Date: Sun, 17 Sep 2023 17:00:57 +0300 Subject: [PATCH 213/250] add layout test --- crates/hir-ty/src/layout/tests/closure.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/hir-ty/src/layout/tests/closure.rs b/crates/hir-ty/src/layout/tests/closure.rs index 576e7f3fc61..bbe855a14de 100644 --- a/crates/hir-ty/src/layout/tests/closure.rs +++ b/crates/hir-ty/src/layout/tests/closure.rs @@ -255,3 +255,17 @@ fn ellipsis_pattern() { } } } + +#[test] +fn regression_15623() { + size_and_align_expr! { + let a = 2; + let b = 3; + let c = 5; + move || { + let 0 = a else { return b; }; + let y = c; + y + } + } +} From c70ee688b3ff2d1c24eda6990576a2dda2b76053 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Sun, 17 Sep 2023 12:05:36 -0400 Subject: [PATCH 214/250] Add me as on vacation --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index d9d523bef39..d35e81c277e 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -585,7 +585,7 @@ cc = ["@nnethercote"] [assign] warn_non_default_branch = true contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html" -users_on_vacation = ["jyn514"] +users_on_vacation = ["jyn514", "jackh726"] [assign.adhoc_groups] compiler-team = [ From e1e1a02f529a1797d77db627882321edf038f78c Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sun, 17 Sep 2023 18:43:54 +0000 Subject: [PATCH 215/250] Update src/librustdoc/markdown.rs Co-authored-by: Michael Howell --- src/librustdoc/markdown.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 1ac3163b2cd..b74a9392f27 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -158,7 +158,7 @@ pub(crate) fn test(options: Options) -> Result<(), String> { collector.set_position(DUMMY_SP); let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); - // For markdown files, it'll be disabled until the feature is enabled by default. + // For markdown files, custom code classes will be disabled until the feature is enabled by default. find_testable_code( &input_str, &mut collector, From 3b817b2810ff7ecacab02f901f2a1a2902e5e72f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 17 Sep 2023 09:35:18 +0200 Subject: [PATCH 216/250] nop_lift macros: ensure that we are using the right interner --- compiler/rustc_middle/src/ty/context.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f7484048757..fcb1a6971e9 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1214,6 +1214,25 @@ macro_rules! nop_lift { impl<'a, 'tcx> Lift<'tcx> for $ty { type Lifted = $lifted; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + // Assert that the set has the right type. + // Given an argument that has an interned type, the return type has the type of + // the corresponding interner set. This won't actually return anything, we're + // just doing this to compute said type! + fn _intern_set_ty_from_interned_ty<'tcx, Inner>( + _x: Interned<'tcx, Inner>, + ) -> InternedSet<'tcx, Inner> { + unreachable!() + } + fn _type_eq(_x: &T, _y: &T) {} + fn _test<'tcx>(x: $lifted, tcx: TyCtxt<'tcx>) { + // If `x` is a newtype around an `Interned`, then `interner` is an + // interner of appropriate type. (Ideally we'd also check that `x` is a + // newtype with just that one field. Not sure how to do that.) + let interner = _intern_set_ty_from_interned_ty(x.0); + // Now check that this is the same type as `interners.$set`. + _type_eq(&interner, &tcx.interners.$set); + } + tcx.interners .$set .contains_pointer_to(&InternedInSet(&*self.0.0)) @@ -1230,6 +1249,11 @@ macro_rules! nop_list_lift { impl<'a, 'tcx> Lift<'tcx> for &'a List<$ty> { type Lifted = &'tcx List<$lifted>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + // Assert that the set has the right type. + if false { + let _x: &InternedSet<'tcx, List<$lifted>> = &tcx.interners.$set; + } + if self.is_empty() { return Some(List::empty()); } From 78846d17c184dcc1f1714689eaa370e1d472c489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Tue, 16 May 2023 17:11:47 +0200 Subject: [PATCH 217/250] Specialize `fmt::Write::write_fmt` for `Sized` types --- library/core/src/fmt/mod.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 9ce6093f1d1..8204b3855bd 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -188,8 +188,28 @@ pub trait Write { /// assert_eq!(&buf, "world"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - fn write_fmt(mut self: &mut Self, args: Arguments<'_>) -> Result { - write(&mut self, args) + fn write_fmt(&mut self, args: Arguments<'_>) -> Result { + // We use a specialization for `Sized` types to avoid an indirection + // through `&mut self` + trait SpecWriteFmt { + fn spec_write_fmt(self, args: Arguments<'_>) -> Result; + } + + impl SpecWriteFmt for &mut W { + #[inline] + default fn spec_write_fmt(mut self, args: Arguments<'_>) -> Result { + write(&mut self, args) + } + } + + impl SpecWriteFmt for &mut W { + #[inline] + fn spec_write_fmt(self, args: Arguments<'_>) -> Result { + write(self, args) + } + } + + self.spec_write_fmt(args) } } From 1281d43942c58766a54ec14e9c11403b94da0272 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 14 Sep 2023 09:24:51 +1000 Subject: [PATCH 218/250] Remove `RegionHighlightMode::tcx`. It's easier to pass it in to the one method that needs it (`highlighting_region_vid`) than to store it in the type. This means `RegionHighlightMode` can impl `Default`. --- .../src/diagnostics/region_name.rs | 8 +++---- .../nice_region_error/placeholder_error.rs | 2 +- .../trait_impl_difference.rs | 8 +++---- compiler/rustc_middle/src/ty/print/pretty.rs | 23 ++++++++----------- 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 337af89b21f..b40e89e471d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -442,8 +442,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { span: Span, counter: usize, ) -> RegionNameHighlight { - let mut highlight = RegionHighlightMode::new(self.infcx.tcx); - highlight.highlighting_region_vid(needle_fr, counter); + let mut highlight = RegionHighlightMode::default(); + highlight.highlighting_region_vid(self.infcx.tcx, needle_fr, counter); let type_name = self.infcx.extract_inference_diagnostics_data(ty.into(), Some(highlight)).name; @@ -804,8 +804,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { return None; } - let mut highlight = RegionHighlightMode::new(tcx); - highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap()); + let mut highlight = RegionHighlightMode::default(); + highlight.highlighting_region_vid(tcx, fr, *self.next_region_name.try_borrow().unwrap()); let type_name = self.infcx.extract_inference_diagnostics_data(yield_ty.into(), Some(highlight)).name; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs index f903f7a49ef..4aec28b051f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -385,7 +385,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { let highlight_trait_ref = |trait_ref| Highlighted { tcx: self.tcx(), - highlight: RegionHighlightMode::new(self.tcx()), + highlight: RegionHighlightMode::default(), value: trait_ref, }; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 12d38ced030..d2ba9966f03 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -67,9 +67,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } impl<'tcx> HighlightBuilder<'tcx> { - fn build(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> { + fn build(ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> { let mut builder = - HighlightBuilder { highlight: RegionHighlightMode::new(tcx), counter: 1 }; + HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1 }; builder.visit_ty(ty); builder.highlight } @@ -85,12 +85,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } } - let expected_highlight = HighlightBuilder::build(self.tcx(), expected); + let expected_highlight = HighlightBuilder::build(expected); let expected = self .cx .extract_inference_diagnostics_data(expected.into(), Some(expected_highlight)) .name; - let found_highlight = HighlightBuilder::build(self.tcx(), found); + let found_highlight = HighlightBuilder::build(found); let found = self.cx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name; diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index f24ac79323f..7a2cf6c4ca4 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -136,10 +136,8 @@ define_helper!( /// /// Regions not selected by the region highlight mode are presently /// unaffected. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub struct RegionHighlightMode<'tcx> { - tcx: TyCtxt<'tcx>, - /// If enabled, when we see the selected region, use "`'N`" /// instead of the ordinary behavior. highlight_regions: [Option<(ty::Region<'tcx>, usize)>; 3], @@ -155,14 +153,6 @@ pub struct RegionHighlightMode<'tcx> { } impl<'tcx> RegionHighlightMode<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>) -> Self { - Self { - tcx, - highlight_regions: Default::default(), - highlight_bound_region: Default::default(), - } - } - /// If `region` and `number` are both `Some`, invokes /// `highlighting_region`. pub fn maybe_highlighting_region( @@ -188,8 +178,13 @@ impl<'tcx> RegionHighlightMode<'tcx> { } /// Convenience wrapper for `highlighting_region`. - pub fn highlighting_region_vid(&mut self, vid: ty::RegionVid, number: usize) { - self.highlighting_region(ty::Region::new_var(self.tcx, vid), number) + pub fn highlighting_region_vid( + &mut self, + tcx: TyCtxt<'tcx>, + vid: ty::RegionVid, + number: usize, + ) { + self.highlighting_region(ty::Region::new_var(tcx, vid), number) } /// Returns `Some(n)` with the number to use for the given region, if any. @@ -1778,7 +1773,7 @@ impl<'a, 'tcx> FmtPrinter<'a, 'tcx> { printed_type_count: 0, type_length_limit, truncated: false, - region_highlight_mode: RegionHighlightMode::new(tcx), + region_highlight_mode: RegionHighlightMode::default(), ty_infer_name_resolver: None, const_infer_name_resolver: None, })) From 46fe65d0e5bc003b9d7b672988e3d10816c61ce8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 14 Sep 2023 10:26:04 +1000 Subject: [PATCH 219/250] Remove unused `Display` impls. --- compiler/rustc_middle/src/ty/print/pretty.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 7a2cf6c4ca4..e1d4e43841d 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2741,20 +2741,14 @@ forward_display_to_print! { // HACK(eddyb) these are exhaustive instead of generic, // because `for<'tcx>` isn't possible yet. - ty::PolyExistentialPredicate<'tcx>, ty::PolyExistentialProjection<'tcx>, ty::PolyExistentialTraitRef<'tcx>, ty::Binder<'tcx, ty::TraitRef<'tcx>>, ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - ty::Binder<'tcx, TraitRefPrintOnlyTraitName<'tcx>>, ty::Binder<'tcx, ty::FnSig<'tcx>>, ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>>, - ty::Binder<'tcx, ty::SubtypePredicate<'tcx>>, ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>, - ty::Binder<'tcx, ty::OutlivesPredicate, ty::Region<'tcx>>>, - ty::Binder<'tcx, ty::OutlivesPredicate, ty::Region<'tcx>>>, - ty::OutlivesPredicate, ty::Region<'tcx>>, ty::OutlivesPredicate, ty::Region<'tcx>> } From 6b1980f9cfdc0d4d4556f1a191041df6dfd7a018 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 14 Sep 2023 12:03:56 +1000 Subject: [PATCH 220/250] Rename `CloneLiftImpls` as `TrivialLiftImpls`. To match `TrivialTypeTraversalImpls` and `TrivialTypeTraversalAndLiftImpls`, and because the `Clone` doesn't mean anything. --- compiler/rustc_middle/src/macros.rs | 4 ++-- compiler/rustc_middle/src/ty/context.rs | 2 +- compiler/rustc_middle/src/ty/structural_impls.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index fca16d8e509..c1884bb8068 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -42,7 +42,7 @@ macro_rules! span_bug { // the impls for you. #[macro_export] -macro_rules! CloneLiftImpls { +macro_rules! TrivialLiftImpls { ($($ty:ty),+ $(,)?) => { $( impl<'tcx> $crate::ty::Lift<'tcx> for $ty { @@ -96,6 +96,6 @@ macro_rules! TrivialTypeTraversalImpls { macro_rules! TrivialTypeTraversalAndLiftImpls { ($($t:tt)*) => { TrivialTypeTraversalImpls! { $($t)* } - CloneLiftImpls! { $($t)* } + TrivialLiftImpls! { $($t)* } } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f7484048757..fa43241ce9b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1259,7 +1259,7 @@ nop_list_lift! {bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariable // This is the impl for `&'a GenericArgs<'a>`. nop_list_lift! {args; GenericArg<'a> => GenericArg<'tcx>} -CloneLiftImpls! { +TrivialLiftImpls! { Constness, traits::WellFormedLoc, ImplPolarity, diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 7c25d0209c9..5d707e8ced1 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -456,7 +456,7 @@ impl<'tcx, T: DebugWithInfcx>> DebugWithInfcx> for ty: // For things for which the type library provides traversal implementations // for all Interners, we only need to provide a Lift implementation: -CloneLiftImpls! { +TrivialLiftImpls! { (), bool, usize, From af7d3e501b2a377f794d45e77fa01de25a988e0e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 14 Sep 2023 09:46:18 +1000 Subject: [PATCH 221/250] Remove unused `Lift` derives. I found these by commenting out all `Lift` derives and then adding back the ones that were necessary to successfully compile. --- compiler/rustc_middle/src/infer/canonical.rs | 8 +++---- compiler/rustc_middle/src/infer/mod.rs | 2 +- .../rustc_middle/src/mir/interpret/mod.rs | 2 +- compiler/rustc_middle/src/mir/mod.rs | 4 ++-- compiler/rustc_middle/src/mir/query.rs | 2 +- compiler/rustc_middle/src/traits/mod.rs | 24 +++++++++---------- compiler/rustc_middle/src/traits/query.rs | 21 +++++++--------- compiler/rustc_middle/src/ty/adjustment.rs | 8 +++---- compiler/rustc_middle/src/ty/consts/kind.rs | 2 +- compiler/rustc_middle/src/ty/error.rs | 4 ++-- compiler/rustc_middle/src/ty/generic_args.rs | 4 ++-- compiler/rustc_middle/src/ty/instance.rs | 3 +++ compiler/rustc_middle/src/ty/mod.rs | 4 ++-- compiler/rustc_middle/src/ty/sty.rs | 4 ++-- .../rustc_middle/src/ty/typeck_results.rs | 4 ++-- .../query/type_op/implied_outlives_bounds.rs | 2 +- .../src/traits/query/type_op/outlives.rs | 2 +- 17 files changed, 49 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index b17f1512886..2fb49f2f005 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -34,7 +34,7 @@ use std::ops::Index; /// variables have been rewritten to "canonical vars". These are /// numbered starting from 0 in order of first appearance. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct Canonical<'tcx, V> { pub value: V, pub max_universe: ty::UniverseIndex, @@ -72,7 +72,7 @@ impl<'tcx> ty::TypeFoldable> for CanonicalVarInfos<'tcx> { /// variables. You will need to supply it later to instantiate the /// canonicalized query response. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct CanonicalVarValues<'tcx> { pub var_values: ty::GenericArgsRef<'tcx>, } @@ -311,7 +311,7 @@ pub enum CanonicalTyVarKind { /// After we execute a query with a canonicalized key, we get back a /// `Canonical>`. You can use /// `instantiate_query_result` to access the data in this result. -#[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct QueryResponse<'tcx, R> { pub var_values: CanonicalVarValues<'tcx>, pub region_constraints: QueryRegionConstraints<'tcx>, @@ -326,7 +326,7 @@ pub struct QueryResponse<'tcx, R> { } #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct QueryRegionConstraints<'tcx> { pub outlives: Vec>, pub member_constraints: Vec>, diff --git a/compiler/rustc_middle/src/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs index 493bb8a6823..1384611e146 100644 --- a/compiler/rustc_middle/src/infer/mod.rs +++ b/compiler/rustc_middle/src/infer/mod.rs @@ -13,7 +13,7 @@ use rustc_span::Span; /// R0 member of [O1..On] /// ``` #[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct MemberConstraint<'tcx> { /// The `DefId` and args of the opaque type causing this constraint. /// Used for error reporting. diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 44d1dcbbe17..8c00746a180 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -162,7 +162,7 @@ pub use self::pointer::{Pointer, PointerArithmetic, Provenance}; /// - A constant /// - A static #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, Lift, TypeFoldable, TypeVisitable)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct GlobalId<'tcx> { /// For a constant or static, the `Instance` of the item itself. /// For a promoted global, the `Instance` of the function they belong to. diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 8df3a79b4d4..36992569cfb 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2295,7 +2295,7 @@ pub struct Constant<'tcx> { } #[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)] -#[derive(Lift, TypeFoldable, TypeVisitable)] +#[derive(TypeFoldable, TypeVisitable)] pub enum ConstantKind<'tcx> { /// This constant came from the type system. /// @@ -2615,7 +2615,7 @@ impl<'tcx> ConstantKind<'tcx> { } /// An unevaluated (potentially generic) constant used in MIR. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)] #[derive(Hash, HashStable, TypeFoldable, TypeVisitable)] pub struct UnevaluatedConst<'tcx> { pub def: DefId, diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 71bec49af93..0c80610b308 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -334,7 +334,7 @@ rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16); /// /// See also `rustc_const_eval::borrow_check::constraints`. #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] -#[derive(TyEncodable, TyDecodable, HashStable, Lift, TypeVisitable, TypeFoldable)] +#[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)] pub enum ConstraintCategory<'tcx> { Return(ReturnConstraint), Yield, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 3465759b913..cde1f6d5634 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -86,7 +86,7 @@ pub enum Reveal { /// /// We do not want to intern this as there are a lot of obligation causes which /// only live for a short period of time. -#[derive(Clone, Debug, PartialEq, Eq, Lift, HashStable, TyEncodable, TyDecodable)] +#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] #[derive(TypeVisitable, TypeFoldable)] pub struct ObligationCause<'tcx> { pub span: Span, @@ -194,7 +194,7 @@ impl<'tcx> ObligationCause<'tcx> { } } -#[derive(Clone, Debug, PartialEq, Eq, Lift, HashStable, TyEncodable, TyDecodable)] +#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] #[derive(TypeVisitable, TypeFoldable)] pub struct UnifyReceiverContext<'tcx> { pub assoc_item: ty::AssocItem, @@ -202,7 +202,7 @@ pub struct UnifyReceiverContext<'tcx> { pub args: GenericArgsRef<'tcx>, } -#[derive(Clone, PartialEq, Eq, Lift, Default, HashStable)] +#[derive(Clone, PartialEq, Eq, Default, HashStable)] #[derive(TypeVisitable, TypeFoldable, TyEncodable, TyDecodable)] pub struct InternedObligationCauseCode<'tcx> { /// `None` for `ObligationCauseCode::MiscObligation` (a common case, occurs ~60% of @@ -238,7 +238,7 @@ impl<'tcx> std::ops::Deref for InternedObligationCauseCode<'tcx> { } } -#[derive(Clone, Debug, PartialEq, Eq, Lift, HashStable, TyEncodable, TyDecodable)] +#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] #[derive(TypeVisitable, TypeFoldable)] pub enum ObligationCauseCode<'tcx> { /// Not well classified or should be obvious from the span. @@ -470,7 +470,7 @@ pub enum WellFormedLoc { }, } -#[derive(Clone, Debug, PartialEq, Eq, Lift, HashStable, TyEncodable, TyDecodable)] +#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] #[derive(TypeVisitable, TypeFoldable)] pub struct ImplDerivedObligationCause<'tcx> { pub derived: DerivedObligationCause<'tcx>, @@ -531,7 +531,7 @@ impl<'tcx> ty::Lift<'tcx> for StatementAsExpression { } } -#[derive(Clone, Debug, PartialEq, Eq, Lift, HashStable, TyEncodable, TyDecodable)] +#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] #[derive(TypeVisitable, TypeFoldable)] pub struct MatchExpressionArmCause<'tcx> { pub arm_block_id: Option, @@ -547,7 +547,7 @@ pub struct MatchExpressionArmCause<'tcx> { } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[derive(Lift, TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)] +#[derive(TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)] pub struct IfExpressionCause<'tcx> { pub then_id: hir::HirId, pub else_id: hir::HirId, @@ -557,7 +557,7 @@ pub struct IfExpressionCause<'tcx> { pub opt_suggest_box_span: Option, } -#[derive(Clone, Debug, PartialEq, Eq, Lift, HashStable, TyEncodable, TyDecodable)] +#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] #[derive(TypeVisitable, TypeFoldable)] pub struct DerivedObligationCause<'tcx> { /// The trait predicate of the parent obligation that led to the @@ -570,7 +570,7 @@ pub struct DerivedObligationCause<'tcx> { pub parent_code: InternedObligationCauseCode<'tcx>, } -#[derive(Clone, Debug, TypeVisitable, Lift)] +#[derive(Clone, Debug, TypeVisitable)] pub enum SelectionError<'tcx> { /// The trait is not implemented. Unimplemented, @@ -593,7 +593,7 @@ pub enum SelectionError<'tcx> { OpaqueTypeAutoTraitLeakageUnknown(DefId), } -#[derive(Clone, Debug, TypeVisitable, Lift)] +#[derive(Clone, Debug, TypeVisitable)] pub struct SelectionOutputTypeParameterMismatch<'tcx> { pub found_trait_ref: ty::PolyTraitRef<'tcx>, pub expected_trait_ref: ty::PolyTraitRef<'tcx>, @@ -638,7 +638,7 @@ pub type SelectionResult<'tcx, T> = Result, SelectionError<'tcx>>; /// ### The type parameter `N` /// /// See explanation on `ImplSourceUserDefinedData`. -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] #[derive(TypeFoldable, TypeVisitable)] pub enum ImplSource<'tcx, N> { /// ImplSource identifying a particular impl. @@ -704,7 +704,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { /// is `Obligation`, as one might expect. During codegen, however, this /// is `()`, because codegen only requires a shallow resolution of an /// impl, and nested obligations are satisfied later. -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] +#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] #[derive(TypeFoldable, TypeVisitable)] pub struct ImplSourceUserDefinedData<'tcx, N> { pub impl_def_id: DefId, diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 950a59e9695..975e3e3ac62 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -17,8 +17,7 @@ pub mod type_op { use crate::ty::{Predicate, Ty, TyCtxt, UserType}; use std::fmt; - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)] - #[derive(TypeFoldable, TypeVisitable)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] pub struct AscribeUserType<'tcx> { pub mir_ty: Ty<'tcx>, pub user_ty: UserType<'tcx>, @@ -30,22 +29,19 @@ pub mod type_op { } } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)] - #[derive(TypeFoldable, TypeVisitable)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] pub struct Eq<'tcx> { pub a: Ty<'tcx>, pub b: Ty<'tcx>, } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)] - #[derive(TypeFoldable, TypeVisitable)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] pub struct Subtype<'tcx> { pub sub: Ty<'tcx>, pub sup: Ty<'tcx>, } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)] - #[derive(TypeFoldable, TypeVisitable)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] pub struct ProvePredicate<'tcx> { pub predicate: Predicate<'tcx>, } @@ -56,8 +52,7 @@ pub mod type_op { } } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)] - #[derive(TypeFoldable, TypeVisitable)] + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] pub struct Normalize { pub value: T, } @@ -101,7 +96,7 @@ impl<'tcx> From> for NoSolution { } } -#[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable)] pub struct DropckOutlivesResult<'tcx> { pub kinds: Vec>, pub overflows: Vec>, @@ -194,7 +189,7 @@ pub struct MethodAutoderefBadTy<'tcx> { } /// Result from the `normalize_projection_ty` query. -#[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct NormalizationResult<'tcx> { /// Result of normalization. pub normalized_ty: Ty<'tcx>, @@ -207,7 +202,7 @@ pub struct NormalizationResult<'tcx> { /// case they are called implied bounds). They are fed to the /// `OutlivesEnv` which in turn is supplied to the region checker and /// other parts of the inference system. -#[derive(Clone, Debug, TypeFoldable, TypeVisitable, Lift, HashStable)] +#[derive(Clone, Debug, TypeFoldable, TypeVisitable, HashStable)] pub enum OutlivesBound<'tcx> { RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), RegionSubParam(ty::Region<'tcx>, ty::ParamTy), diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 76931ceaa69..c3e8991c63a 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -76,7 +76,7 @@ pub enum PointerCoercion { /// At some point, of course, `Box` should move out of the compiler, in which /// case this is analogous to transforming a struct. E.g., `Box<[i32; 4]>` -> /// `Box<[i32]>` is an `Adjust::Unsize` with the target `Box<[i32]>`. -#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct Adjustment<'tcx> { pub kind: Adjust<'tcx>, pub target: Ty<'tcx>, @@ -88,7 +88,7 @@ impl<'tcx> Adjustment<'tcx> { } } -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub enum Adjust<'tcx> { /// Go from ! to any type. NeverToAny, @@ -110,7 +110,7 @@ pub enum Adjust<'tcx> { /// The target type is `U` in both cases, with the region and mutability /// being those shared by both the receiver and the returned reference. #[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] -#[derive(TypeFoldable, TypeVisitable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub struct OverloadedDeref<'tcx> { pub region: ty::Region<'tcx>, pub mutbl: hir::Mutability, @@ -182,7 +182,7 @@ impl From for hir::Mutability { } #[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] -#[derive(TypeFoldable, TypeVisitable, Lift)] +#[derive(TypeFoldable, TypeVisitable)] pub enum AutoBorrow<'tcx> { /// Converts from T to &T. Ref(ty::Region<'tcx>, AutoBorrowMutability), diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index e25402fe0c2..749b54ca0be 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -8,7 +8,7 @@ use rustc_hir::def_id::DefId; use rustc_macros::HashStable; /// An unevaluated (potentially generic) constant used in the type-system. -#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)] +#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)] #[derive(Hash, HashStable, TypeFoldable, TypeVisitable)] pub struct UnevaluatedConst<'tcx> { pub def: DefId, diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index bf6f082c21c..f939d466078 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -11,7 +11,7 @@ use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; use std::path::PathBuf; -#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable, Lift)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable)] pub struct ExpectedFound { pub expected: T, pub found: T, @@ -28,7 +28,7 @@ impl ExpectedFound { } // Data structures used in type unification -#[derive(Copy, Clone, Debug, TypeVisitable, Lift, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, TypeVisitable, PartialEq, Eq)] #[rustc_pass_by_value] pub enum TypeError<'tcx> { Mismatch, diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index e598ead791e..72390e4bbb0 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -1029,7 +1029,7 @@ impl<'a, 'tcx> ArgFolder<'a, 'tcx> { /// Stores the user-given args to reach some fully qualified path /// (e.g., `::Item` or `::Item`). #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct UserArgs<'tcx> { /// The args for the item as given by the user. pub args: GenericArgsRef<'tcx>, @@ -1056,7 +1056,7 @@ pub struct UserArgs<'tcx> { /// the self type, giving `Foo`. Finally, we unify that with /// the self type here, which contains `?A` to be `&'static u32` #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(HashStable, TypeFoldable, TypeVisitable)] pub struct UserSelfTy<'tcx> { pub impl_def_id: DefId, pub self_ty: Ty<'tcx>, diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index e5b9203d12a..2b75f6c4e8f 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -18,6 +18,9 @@ use std::fmt; /// Monomorphization happens on-the-fly and no monomorphized MIR is ever created. Instead, this type /// simply couples a potentially generic `InstanceDef` with some args, and codegen and const eval /// will do all required substitution as they run. +/// +/// Note: the `Lift` impl is currently not used by rustc, but is used by +/// rustc_codegen_cranelift when the `jit` feature is enabled. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable, Lift, TypeFoldable, TypeVisitable)] pub struct Instance<'tcx> { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ff420cf902a..eb8ea0bc114 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1510,7 +1510,7 @@ impl<'a, 'tcx> IntoIterator for &'a InstantiatedPredicates<'tcx> { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable, Lift)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] #[derive(TypeFoldable, TypeVisitable)] pub struct OpaqueTypeKey<'tcx> { pub def_id: LocalDefId, @@ -1793,7 +1793,7 @@ impl<'tcx> ParamEnv<'tcx> { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)] -#[derive(HashStable, Lift)] +#[derive(HashStable)] pub struct ParamEnvAnd<'tcx, T> { pub param_env: ParamEnv<'tcx>, pub value: T, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 2502e303f7a..edab7fe58ba 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -351,7 +351,7 @@ impl<'tcx> ClosureArgs<'tcx> { } /// Similar to `ClosureArgs`; see the above documentation for more. -#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable, Lift)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)] pub struct GeneratorArgs<'tcx> { pub args: GenericArgsRef<'tcx>, } @@ -1305,7 +1305,7 @@ impl<'tcx> AliasTy<'tcx> { } } -#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, Lift)] +#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)] pub struct GenSig<'tcx> { pub resume_ty: Ty<'tcx>, pub yield_ty: Ty<'tcx>, diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 159cbb72a3b..69c4c588c44 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -654,7 +654,7 @@ rustc_index::newtype_index! { pub type CanonicalUserTypeAnnotations<'tcx> = IndexVec>; -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct CanonicalUserTypeAnnotation<'tcx> { pub user_ty: Box>, pub span: Span, @@ -714,7 +714,7 @@ impl<'tcx> CanonicalUserType<'tcx> { /// from constants that are named via paths, like `Foo::
::new` and /// so forth. #[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)] -#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable)] pub enum UserType<'tcx> { Ty(Ty<'tcx>), diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index 979498fb6e6..e415d70479e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -13,7 +13,7 @@ use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::source_map::DUMMY_SP; use smallvec::{smallvec, SmallVec}; -#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct ImpliedOutlivesBounds<'tcx> { pub ty: Ty<'tcx>, } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index 59f4a22ac75..f2c1243f931 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -6,7 +6,7 @@ use crate::traits::ObligationCtxt; use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution}; use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; -#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct DropckOutlives<'tcx> { dropped_ty: Ty<'tcx>, } From abe2a68acd5aa06191345c94c640f3931d1776e0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 14 Sep 2023 12:05:05 +1000 Subject: [PATCH 222/250] Remove more unused `Lift` impls. --- .../rustc_infer/src/infer/free_regions.rs | 9 +- compiler/rustc_middle/src/infer/canonical.rs | 2 +- compiler/rustc_middle/src/mir/basic_blocks.rs | 2 +- .../rustc_middle/src/mir/interpret/error.rs | 2 +- compiler/rustc_middle/src/mir/mod.rs | 2 +- .../rustc_middle/src/mir/type_foldable.rs | 2 +- compiler/rustc_middle/src/traits/mod.rs | 11 +- compiler/rustc_middle/src/traits/select.rs | 2 +- .../rustc_middle/src/ty/abstract_const.rs | 2 +- compiler/rustc_middle/src/ty/binding.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 8 +- .../rustc_middle/src/ty/structural_impls.rs | 117 ++++-------------- 12 files changed, 35 insertions(+), 126 deletions(-) diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs index 2402a7ea7c7..ed1a2a11719 100644 --- a/compiler/rustc_infer/src/infer/free_regions.rs +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -4,7 +4,7 @@ //! and use that to decide when one free region outlives another, and so forth. use rustc_data_structures::transitive_relation::TransitiveRelation; -use rustc_middle::ty::{Lift, Region, TyCtxt}; +use rustc_middle::ty::{Region, TyCtxt}; /// Combines a `FreeRegionMap` and a `TyCtxt`. /// @@ -101,10 +101,3 @@ impl<'tcx> FreeRegionMap<'tcx> { result } } - -impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> { - type Lifted = FreeRegionMap<'tcx>; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option> { - self.relation.maybe_map(|fr| tcx.lift(fr)).map(|relation| FreeRegionMap { relation }) - } -} diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 2fb49f2f005..2f2597d6b22 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -432,7 +432,7 @@ impl<'tcx, V> Canonical<'tcx, V> { pub type QueryOutlivesConstraint<'tcx> = (ty::OutlivesPredicate, Region<'tcx>>, ConstraintCategory<'tcx>); -TrivialTypeTraversalAndLiftImpls! { +TrivialTypeTraversalImpls! { crate::infer::canonical::Certainty, crate::infer::canonical::CanonicalTyVarKind, } diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs index 70d8f3bd54b..cd770c395e4 100644 --- a/compiler/rustc_middle/src/mir/basic_blocks.rs +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -178,7 +178,7 @@ impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> { } } -TrivialTypeTraversalAndLiftImpls! { Cache } +TrivialTypeTraversalImpls! { Cache } impl Encodable for Cache { #[inline] diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 7e3be0b5d5f..2194ef81cec 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -67,7 +67,7 @@ impl Into for ReportedErrorInfo { } } -TrivialTypeTraversalAndLiftImpls! { ErrorHandled } +TrivialTypeTraversalImpls! { ErrorHandled } pub type EvalToAllocationRawResult<'tcx> = Result, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>; diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 36992569cfb..5e749776992 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -744,7 +744,7 @@ pub enum BindingForm<'tcx> { RefForGuard, } -TrivialTypeTraversalAndLiftImpls! { BindingForm<'tcx> } +TrivialTypeTraversalImpls! { BindingForm<'tcx> } mod binding_form_impl { use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index 06874741bb0..8d427fdb6f5 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -5,7 +5,7 @@ use rustc_ast::InlineAsmTemplatePiece; use super::*; use crate::ty; -TrivialTypeTraversalAndLiftImpls! { +TrivialTypeTraversalImpls! { BlockTailInfo, MirPhase, SourceInfo, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index cde1f6d5634..1340e674568 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -13,7 +13,7 @@ use crate::infer::canonical::Canonical; use crate::mir::ConstraintCategory; use crate::ty::abstract_const::NotConstEvaluatable; use crate::ty::GenericArgsRef; -use crate::ty::{self, AdtKind, Ty, TyCtxt}; +use crate::ty::{self, AdtKind, Ty}; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diagnostic}; @@ -524,13 +524,6 @@ pub enum StatementAsExpression { NeedsBoxing, } -impl<'tcx> ty::Lift<'tcx> for StatementAsExpression { - type Lifted = StatementAsExpression; - fn lift_to_tcx(self, _tcx: TyCtxt<'tcx>) -> Option { - Some(self) - } -} - #[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] #[derive(TypeVisitable, TypeFoldable)] pub struct MatchExpressionArmCause<'tcx> { @@ -736,7 +729,7 @@ pub enum BuiltinImplSource { TupleUnsizing, } -TrivialTypeTraversalAndLiftImpls! { BuiltinImplSource } +TrivialTypeTraversalImpls! { BuiltinImplSource } #[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)] pub enum ObjectSafetyViolation { diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index ffae3579889..90bc5dd8f69 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -305,7 +305,7 @@ impl From for OverflowError { } } -TrivialTypeTraversalAndLiftImpls! { OverflowError } +TrivialTypeTraversalImpls! { OverflowError } impl<'tcx> From for SelectionError<'tcx> { fn from(overflow_error: OverflowError) -> SelectionError<'tcx> { diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index cdd8351499b..570f896ba29 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -27,7 +27,7 @@ impl From for NotConstEvaluatable { } } -TrivialTypeTraversalAndLiftImpls! { NotConstEvaluatable } +TrivialTypeTraversalImpls! { NotConstEvaluatable } pub type BoundAbstractConst<'tcx> = Result>>, ErrorGuaranteed>; diff --git a/compiler/rustc_middle/src/ty/binding.rs b/compiler/rustc_middle/src/ty/binding.rs index 2fec8ac9095..af594bc5f24 100644 --- a/compiler/rustc_middle/src/ty/binding.rs +++ b/compiler/rustc_middle/src/ty/binding.rs @@ -6,7 +6,7 @@ pub enum BindingMode { BindByValue(Mutability), } -TrivialTypeTraversalAndLiftImpls! { BindingMode } +TrivialTypeTraversalImpls! { BindingMode } impl BindingMode { pub fn convert(BindingAnnotation(by_ref, mutbl): BindingAnnotation) -> BindingMode { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index fa43241ce9b..880d3029451 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -50,7 +50,7 @@ use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::definitions::Definitions; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{Constness, HirId, Node, TraitCandidate}; +use rustc_hir::{HirId, Node, TraitCandidate}; use rustc_index::IndexVec; use rustc_macros::HashStable; use rustc_query_system::dep_graph::DepNodeIndex; @@ -1251,19 +1251,13 @@ nop_lift! {predicate; Clause<'a> => Clause<'tcx>} nop_list_lift! {type_lists; Ty<'a> => Ty<'tcx>} nop_list_lift! {poly_existential_predicates; PolyExistentialPredicate<'a> => PolyExistentialPredicate<'tcx>} -nop_list_lift! {clauses; Clause<'a> => Clause<'tcx>} -nop_list_lift! {canonical_var_infos; CanonicalVarInfo<'a> => CanonicalVarInfo<'tcx>} -nop_list_lift! {projs; ProjectionKind => ProjectionKind} nop_list_lift! {bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariableKind} // This is the impl for `&'a GenericArgs<'a>`. nop_list_lift! {args; GenericArg<'a> => GenericArg<'tcx>} TrivialLiftImpls! { - Constness, - traits::WellFormedLoc, ImplPolarity, - crate::mir::ReturnConstraint, } macro_rules! sty_debug_print { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 5d707e8ced1..f4158597d10 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -9,14 +9,11 @@ use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer}; use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; use crate::ty::{self, AliasTy, InferConst, Lift, Term, TermKind, Ty, TyCtxt}; use rustc_hir::def::Namespace; -use rustc_index::{Idx, IndexVec}; use rustc_target::abi::TyAndLayout; use rustc_type_ir::{ConstKind, DebugWithInfcx, InferCtxtLike, OptWithInfcx}; use std::fmt::{self, Debug}; use std::ops::ControlFlow; -use std::rc::Rc; -use std::sync::Arc; use super::print::PrettyPrinter; use super::{GenericArg, GenericArgKind, Region}; @@ -457,21 +454,15 @@ impl<'tcx, T: DebugWithInfcx>> DebugWithInfcx> for ty: // For things for which the type library provides traversal implementations // for all Interners, we only need to provide a Lift implementation: TrivialLiftImpls! { - (), - bool, - usize, - u16, - u32, - u64, - String, - rustc_type_ir::DebruijnIndex, + (), + bool, + usize, } -// For things about which the type library does not know, or does not -// provide any traversal implementations, we need to provide both a Lift -// implementation and traversal implementations (the latter only for -// TyCtxt<'_> interners). -TrivialTypeTraversalAndLiftImpls! { +// For some things about which the type library does not know, or does not +// provide any traversal implementations, we need to provide a traversal +// implementation (only for TyCtxt<'_> interners). +TrivialTypeTraversalImpls! { ::rustc_target::abi::FieldIdx, ::rustc_target::abi::VariantIdx, crate::middle::region::Scope, @@ -481,14 +472,10 @@ TrivialTypeTraversalAndLiftImpls! { ::rustc_ast::NodeId, ::rustc_span::symbol::Symbol, ::rustc_hir::def::Res, - ::rustc_hir::def_id::DefId, ::rustc_hir::def_id::LocalDefId, ::rustc_hir::HirId, ::rustc_hir::MatchSource, - ::rustc_hir::Mutability, - ::rustc_hir::Unsafety, ::rustc_target::asm::InlineAsmRegOrRegClass, - ::rustc_target::spec::abi::Abi, crate::mir::coverage::CounterId, crate::mir::coverage::ExpressionId, crate::mir::coverage::MappedExpressionIndex, @@ -506,16 +493,12 @@ TrivialTypeTraversalAndLiftImpls! { crate::ty::AssocItem, crate::ty::AssocKind, crate::ty::AliasKind, - crate::ty::AliasRelationDirection, crate::ty::Placeholder, crate::ty::Placeholder, crate::ty::Placeholder, - crate::ty::ClosureKind, crate::ty::FreeRegion, crate::ty::InferTy, crate::ty::IntVarValue, - crate::ty::ParamConst, - crate::ty::ParamTy, crate::ty::adjustment::PointerCoercion, crate::ty::RegionVid, crate::ty::UniverseIndex, @@ -523,33 +506,30 @@ TrivialTypeTraversalAndLiftImpls! { ::rustc_span::Span, ::rustc_span::symbol::Ident, ::rustc_errors::ErrorGuaranteed, + ty::BoundVar, + ty::ValTree<'tcx>, +} +// For some things about which the type library does not know, or does not +// provide any traversal implementations, we need to provide a traversal +// implementation and a lift implementation (the former only for TyCtxt<'_> +// interners). +TrivialTypeTraversalAndLiftImpls! { + ::rustc_hir::def_id::DefId, + ::rustc_hir::Mutability, + ::rustc_hir::Unsafety, + ::rustc_target::spec::abi::Abi, + crate::ty::AliasRelationDirection, + crate::ty::ClosureKind, + crate::ty::ParamConst, + crate::ty::ParamTy, interpret::Scalar, interpret::AllocId, rustc_target::abi::Size, - ty::BoundVar, -} - -TrivialTypeTraversalAndLiftImpls! { - ty::ValTree<'tcx>, } /////////////////////////////////////////////////////////////////////////// // Lift implementations -impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>> Lift<'tcx> for (A, B) { - type Lifted = (A::Lifted, B::Lifted); - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - Some((tcx.lift(self.0)?, tcx.lift(self.1)?)) - } -} - -impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C) { - type Lifted = (A::Lifted, B::Lifted, C::Lifted); - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - Some((tcx.lift(self.0)?, tcx.lift(self.1)?, tcx.lift(self.2)?)) - } -} - impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option { type Lifted = Option; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { @@ -560,50 +540,6 @@ impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option { } } -impl<'tcx, T: Lift<'tcx>, E: Lift<'tcx>> Lift<'tcx> for Result { - type Lifted = Result; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - match self { - Ok(x) => tcx.lift(x).map(Ok), - Err(e) => tcx.lift(e).map(Err), - } - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Box { - type Lifted = Box; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - Some(Box::new(tcx.lift(*self)?)) - } -} - -impl<'tcx, T: Lift<'tcx> + Clone> Lift<'tcx> for Rc { - type Lifted = Rc; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - Some(Rc::new(tcx.lift(self.as_ref().clone())?)) - } -} - -impl<'tcx, T: Lift<'tcx> + Clone> Lift<'tcx> for Arc { - type Lifted = Arc; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - Some(Arc::new(tcx.lift(self.as_ref().clone())?)) - } -} -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Vec { - type Lifted = Vec; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - self.into_iter().map(|v| tcx.lift(v)).collect() - } -} - -impl<'tcx, I: Idx, T: Lift<'tcx>> Lift<'tcx> for IndexVec { - type Lifted = IndexVec; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - self.into_iter().map(|e| tcx.lift(e)).collect() - } -} - impl<'a, 'tcx> Lift<'tcx> for Term<'a> { type Lifted = ty::Term<'tcx>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { @@ -616,13 +552,6 @@ impl<'a, 'tcx> Lift<'tcx> for Term<'a> { ) } } -impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> { - type Lifted = ty::ParamEnv<'tcx>; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(self.caller_bounds()) - .map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal())) - } -} /////////////////////////////////////////////////////////////////////////// // Traversal implementations. From a716c9620f0e2f2ce7ecd66f5328cd9a59ff9208 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 18 Sep 2023 03:42:35 +0200 Subject: [PATCH 223/250] address review comment See https://github.com/rust-lang/rust/pull/115558#issuecomment-1722601187 --- config.example.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/config.example.toml b/config.example.toml index 95f0c95bc5c..8732b669317 100644 --- a/config.example.toml +++ b/config.example.toml @@ -455,6 +455,7 @@ changelog-seen = 2 # Sets the number of codegen units to build the standard library with, # regardless of what the codegen-unit setting for the rest of the compiler is. +# NOTE: building with anything other than 1 is known to occasionally have bugs. #codegen-units-std = codegen-units # Whether or not debug assertions are enabled for the compiler and standard library. From 1d65446590c7ee7df99f0c8ad8b4f3c0f086c18f Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Mon, 18 Sep 2023 12:50:34 +0800 Subject: [PATCH 224/250] Update cargo --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index d5336f813df..b4ddf95ad99 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit d5336f813df39d476e61fc46daabb1446350660a +Subproject commit b4ddf95ad9954118ac0dae835f2966394ad04c02 From 0522bde4bca81b9f44a7d6b313ed328b78d73c83 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 Sep 2023 12:31:35 +0200 Subject: [PATCH 225/250] simplify inject_impl_of_structural_trait --- .../src/deriving/cmp/eq.rs | 17 +++- .../src/deriving/cmp/partial_eq.rs | 19 ++-- .../src/deriving/generic/mod.rs | 22 +++-- .../rustc_builtin_macros/src/deriving/mod.rs | 98 +------------------ 4 files changed, 42 insertions(+), 114 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index 745358fde4b..a000e4895d1 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -18,6 +18,20 @@ pub fn expand_deriving_eq( is_const: bool, ) { let span = cx.with_def_site_ctxt(span); + + let structural_trait_def = TraitDef { + span, + path: path_std!(marker::StructuralEq), + skip_path_as_bound: true, // crucial! + needs_copy_as_bound_if_packed: false, + additional_bounds: Vec::new(), + supports_unions: true, + methods: Vec::new(), + associated_types: Vec::new(), + is_const: false, + }; + structural_trait_def.expand(cx, mitem, item, push); + let trait_def = TraitDef { span, path: path_std!(cmp::Eq), @@ -44,9 +58,6 @@ pub fn expand_deriving_eq( associated_types: Vec::new(), is_const, }; - - super::inject_impl_of_structural_trait(cx, span, item, path_std!(marker::StructuralEq), push); - trait_def.expand_ext(cx, mitem, item, push, true) } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index a71ecc5db7d..a170468b413 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -72,13 +72,20 @@ pub fn expand_deriving_partial_eq( BlockOrExpr::new_expr(expr) } - super::inject_impl_of_structural_trait( - cx, + let structural_trait_def = TraitDef { span, - item, - path_std!(marker::StructuralPartialEq), - push, - ); + path: path_std!(marker::StructuralPartialEq), + skip_path_as_bound: true, // crucial! + needs_copy_as_bound_if_packed: false, + additional_bounds: Vec::new(), + // We really don't support unions, but that's already checked by the impl generated below; + // a second check here would lead to redundant error messages. + supports_unions: true, + methods: Vec::new(), + associated_types: Vec::new(), + is_const: false, + }; + structural_trait_def.expand(cx, mitem, item, push); // No need to generate `ne`, the default suffices, and not generating it is // faster. diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 6597ee3cf1b..110581f8197 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -711,7 +711,9 @@ impl<'a> TraitDef<'a> { .collect(); // Require the current trait. - bounds.push(cx.trait_bound(trait_path.clone(), self.is_const)); + if !self.skip_path_as_bound { + bounds.push(cx.trait_bound(trait_path.clone(), self.is_const)); + } // Add a `Copy` bound if required. if is_packed && self.needs_copy_as_bound_if_packed { @@ -722,15 +724,17 @@ impl<'a> TraitDef<'a> { )); } - let predicate = ast::WhereBoundPredicate { - span: self.span, - bound_generic_params: field_ty_param.bound_generic_params, - bounded_ty: field_ty_param.ty, - bounds, - }; + if !bounds.is_empty() { + let predicate = ast::WhereBoundPredicate { + span: self.span, + bound_generic_params: field_ty_param.bound_generic_params, + bounded_ty: field_ty_param.ty, + bounds, + }; - let predicate = ast::WherePredicate::BoundPredicate(predicate); - where_clause.predicates.push(predicate); + let predicate = ast::WherePredicate::BoundPredicate(predicate); + where_clause.predicates.push(predicate); + } } } } diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index d34336e7679..a6f3252e7be 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -2,9 +2,9 @@ use rustc_ast as ast; use rustc_ast::ptr::P; -use rustc_ast::{GenericArg, Impl, ItemKind, MetaItem}; +use rustc_ast::{GenericArg, MetaItem}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; -use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use thin_vec::{thin_vec, ThinVec}; @@ -116,100 +116,6 @@ fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P { })) } -// Injects `impl<...> Structural for ItemType<...> { }`. In particular, -// does *not* add `where T: Structural` for parameters `T` in `...`. -// (That's the main reason we cannot use TraitDef here.) -fn inject_impl_of_structural_trait( - cx: &mut ExtCtxt<'_>, - span: Span, - item: &Annotatable, - structural_path: generic::ty::Path, - push: &mut dyn FnMut(Annotatable), -) { - let Annotatable::Item(item) = item else { - unreachable!(); - }; - - let generics = match &item.kind { - ItemKind::Struct(_, generics) | ItemKind::Enum(_, generics) => generics, - // Do not inject `impl Structural for Union`. (`PartialEq` does not - // support unions, so we will see error downstream.) - ItemKind::Union(..) => return, - _ => unreachable!(), - }; - - // Create generics param list for where clauses and impl headers - let mut generics = generics.clone(); - - let ctxt = span.ctxt(); - - // Create the type of `self`. - // - // in addition, remove defaults from generic params (impls cannot have them). - let self_params: Vec<_> = generics - .params - .iter_mut() - .map(|param| match &mut param.kind { - ast::GenericParamKind::Lifetime => ast::GenericArg::Lifetime( - cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident), - ), - ast::GenericParamKind::Type { default } => { - *default = None; - ast::GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident)) - } - ast::GenericParamKind::Const { ty: _, kw_span: _, default } => { - *default = None; - ast::GenericArg::Const( - cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident), - ) - } - }) - .collect(); - - let type_ident = item.ident; - - let trait_ref = cx.trait_ref(structural_path.to_path(cx, span, type_ident, &generics)); - let self_type = cx.ty_path(cx.path_all(span, false, vec![type_ident], self_params)); - - // It would be nice to also encode constraint `where Self: Eq` (by adding it - // onto `generics` cloned above). Unfortunately, that strategy runs afoul of - // rust-lang/rust#48214. So we perform that additional check in the compiler - // itself, instead of encoding it here. - - // Keep the lint and stability attributes of the original item, to control - // how the generated implementation is linted. - let mut attrs = ast::AttrVec::new(); - attrs.extend( - item.attrs - .iter() - .filter(|a| { - [sym::allow, sym::warn, sym::deny, sym::forbid, sym::stable, sym::unstable] - .contains(&a.name_or_empty()) - }) - .cloned(), - ); - // Mark as `automatically_derived` to avoid some silly lints. - attrs.push(cx.attr_word(sym::automatically_derived, span)); - - let newitem = cx.item( - span, - Ident::empty(), - attrs, - ItemKind::Impl(Box::new(Impl { - unsafety: ast::Unsafe::No, - polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Final, - constness: ast::Const::No, - generics, - of_trait: Some(trait_ref), - self_ty: self_type, - items: ThinVec::new(), - })), - ); - - push(Annotatable::Item(newitem)); -} - fn assert_ty_bounds( cx: &mut ExtCtxt<'_>, stmts: &mut ThinVec, From 7b7caae30e2b23055f2cb686b15757904a3d840d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 3 Sep 2023 10:23:04 +0200 Subject: [PATCH 226/250] get rid of duplicate primitive_docs --- library/core/primitive_docs/box_into_raw.md | 1 - library/core/primitive_docs/fs_file.md | 1 - library/core/primitive_docs/io_bufread.md | 1 - library/core/primitive_docs/io_read.md | 1 - library/core/primitive_docs/io_seek.md | 1 - library/core/primitive_docs/io_write.md | 1 - .../core/primitive_docs/net_tosocketaddrs.md | 1 - library/core/primitive_docs/process_exit.md | 1 - library/core/primitive_docs/string_string.md | 1 - library/core/src/primitive_docs.rs | 23 +- library/core/src/tuple.rs | 2 +- library/std/primitive_docs/box_into_raw.md | 1 - library/std/primitive_docs/fs_file.md | 1 - library/std/primitive_docs/io_bufread.md | 1 - library/std/primitive_docs/io_read.md | 1 - library/std/primitive_docs/io_seek.md | 1 - library/std/primitive_docs/io_write.md | 1 - .../std/primitive_docs/net_tosocketaddrs.md | 1 - library/std/primitive_docs/process_exit.md | 1 - library/std/primitive_docs/string_string.md | 1 - library/std/src/lib.rs | 2 +- library/std/src/primitive_docs.rs | 1593 ----------------- src/tools/tidy/src/lib.rs | 1 - src/tools/tidy/src/main.rs | 1 - src/tools/tidy/src/primitive_docs.rs | 17 - 25 files changed, 12 insertions(+), 1645 deletions(-) delete mode 100644 library/core/primitive_docs/box_into_raw.md delete mode 100644 library/core/primitive_docs/fs_file.md delete mode 100644 library/core/primitive_docs/io_bufread.md delete mode 100644 library/core/primitive_docs/io_read.md delete mode 100644 library/core/primitive_docs/io_seek.md delete mode 100644 library/core/primitive_docs/io_write.md delete mode 100644 library/core/primitive_docs/net_tosocketaddrs.md delete mode 100644 library/core/primitive_docs/process_exit.md delete mode 100644 library/core/primitive_docs/string_string.md delete mode 100644 library/std/primitive_docs/box_into_raw.md delete mode 100644 library/std/primitive_docs/fs_file.md delete mode 100644 library/std/primitive_docs/io_bufread.md delete mode 100644 library/std/primitive_docs/io_read.md delete mode 100644 library/std/primitive_docs/io_seek.md delete mode 100644 library/std/primitive_docs/io_write.md delete mode 100644 library/std/primitive_docs/net_tosocketaddrs.md delete mode 100644 library/std/primitive_docs/process_exit.md delete mode 100644 library/std/primitive_docs/string_string.md delete mode 100644 library/std/src/primitive_docs.rs delete mode 100644 src/tools/tidy/src/primitive_docs.rs diff --git a/library/core/primitive_docs/box_into_raw.md b/library/core/primitive_docs/box_into_raw.md deleted file mode 100644 index 9dd0344c7c7..00000000000 --- a/library/core/primitive_docs/box_into_raw.md +++ /dev/null @@ -1 +0,0 @@ -../std/boxed/struct.Box.html#method.into_raw diff --git a/library/core/primitive_docs/fs_file.md b/library/core/primitive_docs/fs_file.md deleted file mode 100644 index 4023e340a51..00000000000 --- a/library/core/primitive_docs/fs_file.md +++ /dev/null @@ -1 +0,0 @@ -../std/fs/struct.File.html diff --git a/library/core/primitive_docs/io_bufread.md b/library/core/primitive_docs/io_bufread.md deleted file mode 100644 index 7beda2cd390..00000000000 --- a/library/core/primitive_docs/io_bufread.md +++ /dev/null @@ -1 +0,0 @@ -../std/io/trait.BufRead.html diff --git a/library/core/primitive_docs/io_read.md b/library/core/primitive_docs/io_read.md deleted file mode 100644 index b7ecf5e273c..00000000000 --- a/library/core/primitive_docs/io_read.md +++ /dev/null @@ -1 +0,0 @@ -../std/io/trait.Read.html diff --git a/library/core/primitive_docs/io_seek.md b/library/core/primitive_docs/io_seek.md deleted file mode 100644 index db0274d291c..00000000000 --- a/library/core/primitive_docs/io_seek.md +++ /dev/null @@ -1 +0,0 @@ -../std/io/trait.Seek.html diff --git a/library/core/primitive_docs/io_write.md b/library/core/primitive_docs/io_write.md deleted file mode 100644 index 92a3b88a79c..00000000000 --- a/library/core/primitive_docs/io_write.md +++ /dev/null @@ -1 +0,0 @@ -../std/io/trait.Write.html diff --git a/library/core/primitive_docs/net_tosocketaddrs.md b/library/core/primitive_docs/net_tosocketaddrs.md deleted file mode 100644 index 4daa10ddbe2..00000000000 --- a/library/core/primitive_docs/net_tosocketaddrs.md +++ /dev/null @@ -1 +0,0 @@ -../std/net/trait.ToSocketAddrs.html diff --git a/library/core/primitive_docs/process_exit.md b/library/core/primitive_docs/process_exit.md deleted file mode 100644 index cae34d12d52..00000000000 --- a/library/core/primitive_docs/process_exit.md +++ /dev/null @@ -1 +0,0 @@ -../std/process/fn.exit.html diff --git a/library/core/primitive_docs/string_string.md b/library/core/primitive_docs/string_string.md deleted file mode 100644 index 303dc07b185..00000000000 --- a/library/core/primitive_docs/string_string.md +++ /dev/null @@ -1 +0,0 @@ -../std/string/struct.String.html diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 2cfa896e5b9..fd5fe5a04f4 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1,6 +1,3 @@ -// `library/{std,core}/src/primitive_docs.rs` should have the same contents. -// These are different files so that relative links work properly without -// having to have `CARGO_PKG_NAME` set, but conceptually they should always be the same. #[rustc_doc_primitive = "bool"] #[doc(alias = "true")] #[doc(alias = "false")] @@ -106,7 +103,7 @@ mod prim_bool {} /// behaviour of the `!` type - expressions with type `!` will coerce into any other type. /// /// [`u32`]: prim@u32 -#[doc = concat!("[`exit`]: ", include_str!("../primitive_docs/process_exit.md"))] +/// [`exit`]: ../std/process/fn.exit.html /// /// # `!` and generics /// @@ -191,7 +188,7 @@ mod prim_bool {} /// because `!` coerces to `Result` automatically. /// /// [`String::from_str`]: str::FromStr::from_str -#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))] +/// [`String`]: ../std/string/struct.String.html /// [`FromStr`]: str::FromStr /// /// # `!` and traits @@ -267,7 +264,7 @@ mod prim_bool {} /// `impl` for this which simply panics, but the same is true for any type (we could `impl /// Default` for (eg.) [`File`] by just making [`default()`] panic.) /// -#[doc = concat!("[`File`]: ", include_str!("../primitive_docs/fs_file.md"))] +/// [`File`]: ../std/fs/struct.File.html /// [`Debug`]: fmt::Debug /// [`default()`]: Default::default /// @@ -355,7 +352,7 @@ mod prim_never {} /// assert_eq!(5, s.len() * std::mem::size_of::()); /// ``` /// -#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))] +/// [`String`]: ../std/string/struct.String.html /// /// As always, remember that a human intuition for 'character' might not map to /// Unicode's definitions. For example, despite looking similar, the 'é' @@ -572,7 +569,7 @@ impl Copy for () { /// [`null_mut`]: ptr::null_mut /// [`is_null`]: pointer::is_null /// [`offset`]: pointer::offset -#[doc = concat!("[`into_raw`]: ", include_str!("../primitive_docs/box_into_raw.md"))] +/// [`into_raw`]: ../std/boxed/struct.Box.html#method.into_raw /// [`write`]: ptr::write #[stable(feature = "rust1", since = "1.0.0")] mod prim_pointer {} @@ -1361,7 +1358,7 @@ mod prim_usize {} /// /// [`std::fmt`]: fmt /// [`Hash`]: hash::Hash -#[doc = concat!("[`ToSocketAddrs`]: ", include_str!("../primitive_docs/net_tosocketaddrs.md"))] +/// [`ToSocketAddrs`]: ../std/net/trait.ToSocketAddrs.html /// /// `&mut T` references get all of the above except `ToSocketAddrs`, plus the following, if `T` /// implements that trait: @@ -1381,10 +1378,10 @@ mod prim_usize {} /// /// [`FusedIterator`]: iter::FusedIterator /// [`TrustedLen`]: iter::TrustedLen -#[doc = concat!("[`Seek`]: ", include_str!("../primitive_docs/io_seek.md"))] -#[doc = concat!("[`BufRead`]: ", include_str!("../primitive_docs/io_bufread.md"))] -#[doc = concat!("[`Read`]: ", include_str!("../primitive_docs/io_read.md"))] -#[doc = concat!("[`io::Write`]: ", include_str!("../primitive_docs/io_write.md"))] +/// [`Seek`]: ../std/io/trait.Seek.html +/// [`BufRead`]: ../std/io/trait.BufRead.html +/// [`Read`]: ../std/io/trait.Read.html +/// [`io::Write`]: ../std/io/trait.Write.html /// /// Note that due to method call deref coercion, simply calling a trait method will act like they /// work on references as well as they do on owned values! The implementations described here are diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 7782ace69c1..ff292ff2dcb 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -1,4 +1,4 @@ -// See src/libstd/primitive_docs.rs for documentation. +// See core/src/primitive_docs.rs for documentation. use crate::cmp::Ordering::{self, *}; use crate::marker::ConstParamTy; diff --git a/library/std/primitive_docs/box_into_raw.md b/library/std/primitive_docs/box_into_raw.md deleted file mode 100644 index 307b9c85bd6..00000000000 --- a/library/std/primitive_docs/box_into_raw.md +++ /dev/null @@ -1 +0,0 @@ -Box::into_raw diff --git a/library/std/primitive_docs/fs_file.md b/library/std/primitive_docs/fs_file.md deleted file mode 100644 index 13e4540835e..00000000000 --- a/library/std/primitive_docs/fs_file.md +++ /dev/null @@ -1 +0,0 @@ -fs::File diff --git a/library/std/primitive_docs/io_bufread.md b/library/std/primitive_docs/io_bufread.md deleted file mode 100644 index bb688e3a5cc..00000000000 --- a/library/std/primitive_docs/io_bufread.md +++ /dev/null @@ -1 +0,0 @@ -io::BufRead diff --git a/library/std/primitive_docs/io_read.md b/library/std/primitive_docs/io_read.md deleted file mode 100644 index 5118d7c4888..00000000000 --- a/library/std/primitive_docs/io_read.md +++ /dev/null @@ -1 +0,0 @@ -io::Read diff --git a/library/std/primitive_docs/io_seek.md b/library/std/primitive_docs/io_seek.md deleted file mode 100644 index 122e6df77b6..00000000000 --- a/library/std/primitive_docs/io_seek.md +++ /dev/null @@ -1 +0,0 @@ -io::Seek diff --git a/library/std/primitive_docs/io_write.md b/library/std/primitive_docs/io_write.md deleted file mode 100644 index 15dfc907a65..00000000000 --- a/library/std/primitive_docs/io_write.md +++ /dev/null @@ -1 +0,0 @@ -io::Write diff --git a/library/std/primitive_docs/net_tosocketaddrs.md b/library/std/primitive_docs/net_tosocketaddrs.md deleted file mode 100644 index a01f318e887..00000000000 --- a/library/std/primitive_docs/net_tosocketaddrs.md +++ /dev/null @@ -1 +0,0 @@ -net::ToSocketAddrs diff --git a/library/std/primitive_docs/process_exit.md b/library/std/primitive_docs/process_exit.md deleted file mode 100644 index 565a71375cd..00000000000 --- a/library/std/primitive_docs/process_exit.md +++ /dev/null @@ -1 +0,0 @@ -process::exit diff --git a/library/std/primitive_docs/string_string.md b/library/std/primitive_docs/string_string.md deleted file mode 100644 index ce7815ff91b..00000000000 --- a/library/std/primitive_docs/string_string.md +++ /dev/null @@ -1 +0,0 @@ -string::String diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 22ccfc0dbe9..55c112c7b80 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -673,7 +673,7 @@ pub use core::primitive; // Include a number of private modules that exist solely to provide // the rustdoc documentation for primitive types. Using `include!` // because rustdoc only looks for these modules at the crate level. -include!("primitive_docs.rs"); +include!("../../core/src/primitive_docs.rs"); // Include a number of private modules that exist solely to provide // the rustdoc documentation for the existing keywords. Using `include!` diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs deleted file mode 100644 index 2cfa896e5b9..00000000000 --- a/library/std/src/primitive_docs.rs +++ /dev/null @@ -1,1593 +0,0 @@ -// `library/{std,core}/src/primitive_docs.rs` should have the same contents. -// These are different files so that relative links work properly without -// having to have `CARGO_PKG_NAME` set, but conceptually they should always be the same. -#[rustc_doc_primitive = "bool"] -#[doc(alias = "true")] -#[doc(alias = "false")] -/// The boolean type. -/// -/// The `bool` represents a value, which could only be either [`true`] or [`false`]. If you cast -/// a `bool` into an integer, [`true`] will be 1 and [`false`] will be 0. -/// -/// # Basic usage -/// -/// `bool` implements various traits, such as [`BitAnd`], [`BitOr`], [`Not`], etc., -/// which allow us to perform boolean operations using `&`, `|` and `!`. -/// -/// [`if`] requires a `bool` value as its conditional. [`assert!`], which is an -/// important macro in testing, checks whether an expression is [`true`] and panics -/// if it isn't. -/// -/// ``` -/// let bool_val = true & false | false; -/// assert!(!bool_val); -/// ``` -/// -/// [`true`]: ../std/keyword.true.html -/// [`false`]: ../std/keyword.false.html -/// [`BitAnd`]: ops::BitAnd -/// [`BitOr`]: ops::BitOr -/// [`Not`]: ops::Not -/// [`if`]: ../std/keyword.if.html -/// -/// # Examples -/// -/// A trivial example of the usage of `bool`: -/// -/// ``` -/// let praise_the_borrow_checker = true; -/// -/// // using the `if` conditional -/// if praise_the_borrow_checker { -/// println!("oh, yeah!"); -/// } else { -/// println!("what?!!"); -/// } -/// -/// // ... or, a match pattern -/// match praise_the_borrow_checker { -/// true => println!("keep praising!"), -/// false => println!("you should praise!"), -/// } -/// ``` -/// -/// Also, since `bool` implements the [`Copy`] trait, we don't -/// have to worry about the move semantics (just like the integer and float primitives). -/// -/// Now an example of `bool` cast to integer type: -/// -/// ``` -/// assert_eq!(true as i32, 1); -/// assert_eq!(false as i32, 0); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_bool {} - -#[rustc_doc_primitive = "never"] -#[doc(alias = "!")] -// -/// The `!` type, also called "never". -/// -/// `!` represents the type of computations which never resolve to any value at all. For example, -/// the [`exit`] function `fn exit(code: i32) -> !` exits the process without ever returning, and -/// so returns `!`. -/// -/// `break`, `continue` and `return` expressions also have type `!`. For example we are allowed to -/// write: -/// -/// ``` -/// #![feature(never_type)] -/// # fn foo() -> u32 { -/// let x: ! = { -/// return 123 -/// }; -/// # } -/// ``` -/// -/// Although the `let` is pointless here, it illustrates the meaning of `!`. Since `x` is never -/// assigned a value (because `return` returns from the entire function), `x` can be given type -/// `!`. We could also replace `return 123` with a `panic!` or a never-ending `loop` and this code -/// would still be valid. -/// -/// A more realistic usage of `!` is in this code: -/// -/// ``` -/// # fn get_a_number() -> Option { None } -/// # loop { -/// let num: u32 = match get_a_number() { -/// Some(num) => num, -/// None => break, -/// }; -/// # } -/// ``` -/// -/// Both match arms must produce values of type [`u32`], but since `break` never produces a value -/// at all we know it can never produce a value which isn't a [`u32`]. This illustrates another -/// behaviour of the `!` type - expressions with type `!` will coerce into any other type. -/// -/// [`u32`]: prim@u32 -#[doc = concat!("[`exit`]: ", include_str!("../primitive_docs/process_exit.md"))] -/// -/// # `!` and generics -/// -/// ## Infallible errors -/// -/// The main place you'll see `!` used explicitly is in generic code. Consider the [`FromStr`] -/// trait: -/// -/// ``` -/// trait FromStr: Sized { -/// type Err; -/// fn from_str(s: &str) -> Result; -/// } -/// ``` -/// -/// When implementing this trait for [`String`] we need to pick a type for [`Err`]. And since -/// converting a string into a string will never result in an error, the appropriate type is `!`. -/// (Currently the type actually used is an enum with no variants, though this is only because `!` -/// was added to Rust at a later date and it may change in the future.) With an [`Err`] type of -/// `!`, if we have to call [`String::from_str`] for some reason the result will be a -/// [`Result`] which we can unpack like this: -/// -/// ``` -/// #![feature(exhaustive_patterns)] -/// use std::str::FromStr; -/// let Ok(s) = String::from_str("hello"); -/// ``` -/// -/// Since the [`Err`] variant contains a `!`, it can never occur. If the `exhaustive_patterns` -/// feature is present this means we can exhaustively match on [`Result`] by just taking the -/// [`Ok`] variant. This illustrates another behaviour of `!` - it can be used to "delete" certain -/// enum variants from generic types like `Result`. -/// -/// ## Infinite loops -/// -/// While [`Result`] is very useful for removing errors, `!` can also be used to remove -/// successes as well. If we think of [`Result`] as "if this function returns, it has not -/// errored," we get a very intuitive idea of [`Result`] as well: if the function returns, it -/// *has* errored. -/// -/// For example, consider the case of a simple web server, which can be simplified to: -/// -/// ```ignore (hypothetical-example) -/// loop { -/// let (client, request) = get_request().expect("disconnected"); -/// let response = request.process(); -/// response.send(client); -/// } -/// ``` -/// -/// Currently, this isn't ideal, because we simply panic whenever we fail to get a new connection. -/// Instead, we'd like to keep track of this error, like this: -/// -/// ```ignore (hypothetical-example) -/// loop { -/// match get_request() { -/// Err(err) => break err, -/// Ok((client, request)) => { -/// let response = request.process(); -/// response.send(client); -/// }, -/// } -/// } -/// ``` -/// -/// Now, when the server disconnects, we exit the loop with an error instead of panicking. While it -/// might be intuitive to simply return the error, we might want to wrap it in a [`Result`] -/// instead: -/// -/// ```ignore (hypothetical-example) -/// fn server_loop() -> Result { -/// loop { -/// let (client, request) = get_request()?; -/// let response = request.process(); -/// response.send(client); -/// } -/// } -/// ``` -/// -/// Now, we can use `?` instead of `match`, and the return type makes a lot more sense: if the loop -/// ever stops, it means that an error occurred. We don't even have to wrap the loop in an `Ok` -/// because `!` coerces to `Result` automatically. -/// -/// [`String::from_str`]: str::FromStr::from_str -#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))] -/// [`FromStr`]: str::FromStr -/// -/// # `!` and traits -/// -/// When writing your own traits, `!` should have an `impl` whenever there is an obvious `impl` -/// which doesn't `panic!`. The reason is that functions returning an `impl Trait` where `!` -/// does not have an `impl` of `Trait` cannot diverge as their only possible code path. In other -/// words, they can't return `!` from every code path. As an example, this code doesn't compile: -/// -/// ```compile_fail -/// use std::ops::Add; -/// -/// fn foo() -> impl Add { -/// unimplemented!() -/// } -/// ``` -/// -/// But this code does: -/// -/// ``` -/// use std::ops::Add; -/// -/// fn foo() -> impl Add { -/// if true { -/// unimplemented!() -/// } else { -/// 0 -/// } -/// } -/// ``` -/// -/// The reason is that, in the first example, there are many possible types that `!` could coerce -/// to, because many types implement `Add`. However, in the second example, -/// the `else` branch returns a `0`, which the compiler infers from the return type to be of type -/// `u32`. Since `u32` is a concrete type, `!` can and will be coerced to it. See issue [#36375] -/// for more information on this quirk of `!`. -/// -/// [#36375]: https://github.com/rust-lang/rust/issues/36375 -/// -/// As it turns out, though, most traits can have an `impl` for `!`. Take [`Debug`] -/// for example: -/// -/// ``` -/// #![feature(never_type)] -/// # use std::fmt; -/// # trait Debug { -/// # fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result; -/// # } -/// impl Debug for ! { -/// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { -/// *self -/// } -/// } -/// ``` -/// -/// Once again we're using `!`'s ability to coerce into any other type, in this case -/// [`fmt::Result`]. Since this method takes a `&!` as an argument we know that it can never be -/// called (because there is no value of type `!` for it to be called with). Writing `*self` -/// essentially tells the compiler "We know that this code can never be run, so just treat the -/// entire function body as having type [`fmt::Result`]". This pattern can be used a lot when -/// implementing traits for `!`. Generally, any trait which only has methods which take a `self` -/// parameter should have such an impl. -/// -/// On the other hand, one trait which would not be appropriate to implement is [`Default`]: -/// -/// ``` -/// trait Default { -/// fn default() -> Self; -/// } -/// ``` -/// -/// Since `!` has no values, it has no default value either. It's true that we could write an -/// `impl` for this which simply panics, but the same is true for any type (we could `impl -/// Default` for (eg.) [`File`] by just making [`default()`] panic.) -/// -#[doc = concat!("[`File`]: ", include_str!("../primitive_docs/fs_file.md"))] -/// [`Debug`]: fmt::Debug -/// [`default()`]: Default::default -/// -#[unstable(feature = "never_type", issue = "35121")] -mod prim_never {} - -#[rustc_doc_primitive = "char"] -#[allow(rustdoc::invalid_rust_codeblocks)] -/// A character type. -/// -/// The `char` type represents a single character. More specifically, since -/// 'character' isn't a well-defined concept in Unicode, `char` is a '[Unicode -/// scalar value]'. -/// -/// This documentation describes a number of methods and trait implementations on the -/// `char` type. For technical reasons, there is additional, separate -/// documentation in [the `std::char` module](char/index.html) as well. -/// -/// # Validity -/// -/// A `char` is a '[Unicode scalar value]', which is any '[Unicode code point]' -/// other than a [surrogate code point]. This has a fixed numerical definition: -/// code points are in the range 0 to 0x10FFFF, inclusive. -/// Surrogate code points, used by UTF-16, are in the range 0xD800 to 0xDFFF. -/// -/// No `char` may be constructed, whether as a literal or at runtime, that is not a -/// Unicode scalar value: -/// -/// ```compile_fail -/// // Each of these is a compiler error -/// ['\u{D800}', '\u{DFFF}', '\u{110000}']; -/// ``` -/// -/// ```should_panic -/// // Panics; from_u32 returns None. -/// char::from_u32(0xDE01).unwrap(); -/// ``` -/// -/// ```no_run -/// // Undefined behaviour -/// let _ = unsafe { char::from_u32_unchecked(0x110000) }; -/// ``` -/// -/// USVs are also the exact set of values that may be encoded in UTF-8. Because -/// `char` values are USVs and `str` values are valid UTF-8, it is safe to store -/// any `char` in a `str` or read any character from a `str` as a `char`. -/// -/// The gap in valid `char` values is understood by the compiler, so in the -/// below example the two ranges are understood to cover the whole range of -/// possible `char` values and there is no error for a [non-exhaustive match]. -/// -/// ``` -/// let c: char = 'a'; -/// match c { -/// '\0' ..= '\u{D7FF}' => false, -/// '\u{E000}' ..= '\u{10FFFF}' => true, -/// }; -/// ``` -/// -/// All USVs are valid `char` values, but not all of them represent a real -/// character. Many USVs are not currently assigned to a character, but may be -/// in the future ("reserved"); some will never be a character -/// ("noncharacters"); and some may be given different meanings by different -/// users ("private use"). -/// -/// [Unicode code point]: https://www.unicode.org/glossary/#code_point -/// [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value -/// [non-exhaustive match]: ../book/ch06-02-match.html#matches-are-exhaustive -/// [surrogate code point]: https://www.unicode.org/glossary/#surrogate_code_point -/// -/// # Representation -/// -/// `char` is always four bytes in size. This is a different representation than -/// a given character would have as part of a [`String`]. For example: -/// -/// ``` -/// let v = vec!['h', 'e', 'l', 'l', 'o']; -/// -/// // five elements times four bytes for each element -/// assert_eq!(20, v.len() * std::mem::size_of::()); -/// -/// let s = String::from("hello"); -/// -/// // five elements times one byte per element -/// assert_eq!(5, s.len() * std::mem::size_of::()); -/// ``` -/// -#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))] -/// -/// As always, remember that a human intuition for 'character' might not map to -/// Unicode's definitions. For example, despite looking similar, the 'é' -/// character is one Unicode code point while 'é' is two Unicode code points: -/// -/// ``` -/// let mut chars = "é".chars(); -/// // U+00e9: 'latin small letter e with acute' -/// assert_eq!(Some('\u{00e9}'), chars.next()); -/// assert_eq!(None, chars.next()); -/// -/// let mut chars = "é".chars(); -/// // U+0065: 'latin small letter e' -/// assert_eq!(Some('\u{0065}'), chars.next()); -/// // U+0301: 'combining acute accent' -/// assert_eq!(Some('\u{0301}'), chars.next()); -/// assert_eq!(None, chars.next()); -/// ``` -/// -/// This means that the contents of the first string above _will_ fit into a -/// `char` while the contents of the second string _will not_. Trying to create -/// a `char` literal with the contents of the second string gives an error: -/// -/// ```text -/// error: character literal may only contain one codepoint: 'é' -/// let c = 'é'; -/// ^^^ -/// ``` -/// -/// Another implication of the 4-byte fixed size of a `char` is that -/// per-`char` processing can end up using a lot more memory: -/// -/// ``` -/// let s = String::from("love: ❤️"); -/// let v: Vec = s.chars().collect(); -/// -/// assert_eq!(12, std::mem::size_of_val(&s[..])); -/// assert_eq!(32, std::mem::size_of_val(&v[..])); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_char {} - -#[rustc_doc_primitive = "unit"] -#[doc(alias = "(")] -#[doc(alias = ")")] -#[doc(alias = "()")] -// -/// The `()` type, also called "unit". -/// -/// The `()` type has exactly one value `()`, and is used when there -/// is no other meaningful value that could be returned. `()` is most -/// commonly seen implicitly: functions without a `-> ...` implicitly -/// have return type `()`, that is, these are equivalent: -/// -/// ```rust -/// fn long() -> () {} -/// -/// fn short() {} -/// ``` -/// -/// The semicolon `;` can be used to discard the result of an -/// expression at the end of a block, making the expression (and thus -/// the block) evaluate to `()`. For example, -/// -/// ```rust -/// fn returns_i64() -> i64 { -/// 1i64 -/// } -/// fn returns_unit() { -/// 1i64; -/// } -/// -/// let is_i64 = { -/// returns_i64() -/// }; -/// let is_unit = { -/// returns_i64(); -/// }; -/// ``` -/// -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_unit {} - -// Required to make auto trait impls render. -// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls -#[doc(hidden)] -impl () {} - -// Fake impl that's only really used for docs. -#[cfg(doc)] -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for () { - fn clone(&self) -> Self { - loop {} - } -} - -// Fake impl that's only really used for docs. -#[cfg(doc)] -#[stable(feature = "rust1", since = "1.0.0")] -impl Copy for () { - // empty -} - -#[rustc_doc_primitive = "pointer"] -#[doc(alias = "ptr")] -#[doc(alias = "*")] -#[doc(alias = "*const")] -#[doc(alias = "*mut")] -// -/// Raw, unsafe pointers, `*const T`, and `*mut T`. -/// -/// *[See also the `std::ptr` module](ptr).* -/// -/// Working with raw pointers in Rust is uncommon, typically limited to a few patterns. -/// Raw pointers can be unaligned or [`null`]. However, when a raw pointer is -/// dereferenced (using the `*` operator), it must be non-null and aligned. -/// -/// Storing through a raw pointer using `*ptr = data` calls `drop` on the old value, so -/// [`write`] must be used if the type has drop glue and memory is not already -/// initialized - otherwise `drop` would be called on the uninitialized memory. -/// -/// Use the [`null`] and [`null_mut`] functions to create null pointers, and the -/// [`is_null`] method of the `*const T` and `*mut T` types to check for null. -/// The `*const T` and `*mut T` types also define the [`offset`] method, for -/// pointer math. -/// -/// # Common ways to create raw pointers -/// -/// ## 1. Coerce a reference (`&T`) or mutable reference (`&mut T`). -/// -/// ``` -/// let my_num: i32 = 10; -/// let my_num_ptr: *const i32 = &my_num; -/// let mut my_speed: i32 = 88; -/// let my_speed_ptr: *mut i32 = &mut my_speed; -/// ``` -/// -/// To get a pointer to a boxed value, dereference the box: -/// -/// ``` -/// let my_num: Box = Box::new(10); -/// let my_num_ptr: *const i32 = &*my_num; -/// let mut my_speed: Box = Box::new(88); -/// let my_speed_ptr: *mut i32 = &mut *my_speed; -/// ``` -/// -/// This does not take ownership of the original allocation -/// and requires no resource management later, -/// but you must not use the pointer after its lifetime. -/// -/// ## 2. Consume a box (`Box`). -/// -/// The [`into_raw`] function consumes a box and returns -/// the raw pointer. It doesn't destroy `T` or deallocate any memory. -/// -/// ``` -/// let my_speed: Box = Box::new(88); -/// let my_speed: *mut i32 = Box::into_raw(my_speed); -/// -/// // By taking ownership of the original `Box` though -/// // we are obligated to put it together later to be destroyed. -/// unsafe { -/// drop(Box::from_raw(my_speed)); -/// } -/// ``` -/// -/// Note that here the call to [`drop`] is for clarity - it indicates -/// that we are done with the given value and it should be destroyed. -/// -/// ## 3. Create it using `ptr::addr_of!` -/// -/// Instead of coercing a reference to a raw pointer, you can use the macros -/// [`ptr::addr_of!`] (for `*const T`) and [`ptr::addr_of_mut!`] (for `*mut T`). -/// These macros allow you to create raw pointers to fields to which you cannot -/// create a reference (without causing undefined behaviour), such as an -/// unaligned field. This might be necessary if packed structs or uninitialized -/// memory is involved. -/// -/// ``` -/// #[derive(Debug, Default, Copy, Clone)] -/// #[repr(C, packed)] -/// struct S { -/// aligned: u8, -/// unaligned: u32, -/// } -/// let s = S::default(); -/// let p = std::ptr::addr_of!(s.unaligned); // not allowed with coercion -/// ``` -/// -/// ## 4. Get it from C. -/// -/// ``` -/// # #![feature(rustc_private)] -/// #[allow(unused_extern_crates)] -/// extern crate libc; -/// -/// use std::mem; -/// -/// unsafe { -/// let my_num: *mut i32 = libc::malloc(mem::size_of::()) as *mut i32; -/// if my_num.is_null() { -/// panic!("failed to allocate memory"); -/// } -/// libc::free(my_num as *mut libc::c_void); -/// } -/// ``` -/// -/// Usually you wouldn't literally use `malloc` and `free` from Rust, -/// but C APIs hand out a lot of pointers generally, so are a common source -/// of raw pointers in Rust. -/// -/// [`null`]: ptr::null -/// [`null_mut`]: ptr::null_mut -/// [`is_null`]: pointer::is_null -/// [`offset`]: pointer::offset -#[doc = concat!("[`into_raw`]: ", include_str!("../primitive_docs/box_into_raw.md"))] -/// [`write`]: ptr::write -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_pointer {} - -#[rustc_doc_primitive = "array"] -#[doc(alias = "[]")] -#[doc(alias = "[T;N]")] // unfortunately, rustdoc doesn't have fuzzy search for aliases -#[doc(alias = "[T; N]")] -/// A fixed-size array, denoted `[T; N]`, for the element type, `T`, and the -/// non-negative compile-time constant size, `N`. -/// -/// There are two syntactic forms for creating an array: -/// -/// * A list with each element, i.e., `[x, y, z]`. -/// * A repeat expression `[expr; N]` where `N` is how many times to repeat `expr` in the array. `expr` must either be: -/// -/// * A value of a type implementing the [`Copy`] trait -/// * A `const` value -/// -/// Note that `[expr; 0]` is allowed, and produces an empty array. -/// This will still evaluate `expr`, however, and immediately drop the resulting value, so -/// be mindful of side effects. -/// -/// Arrays of *any* size implement the following traits if the element type allows it: -/// -/// - [`Copy`] -/// - [`Clone`] -/// - [`Debug`] -/// - [`IntoIterator`] (implemented for `[T; N]`, `&[T; N]` and `&mut [T; N]`) -/// - [`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`] -/// - [`Hash`] -/// - [`AsRef`], [`AsMut`] -/// - [`Borrow`], [`BorrowMut`] -/// -/// Arrays of sizes from 0 to 32 (inclusive) implement the [`Default`] trait -/// if the element type allows it. As a stopgap, trait implementations are -/// statically generated up to size 32. -/// -/// Arrays of sizes from 1 to 12 (inclusive) implement [`From`], where `Tuple` -/// is a homogeneous [prim@tuple] of appropriate length. -/// -/// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on -/// an array. Indeed, this provides most of the API for working with arrays. -/// -/// Slices have a dynamic size and do not coerce to arrays. Instead, use -/// `slice.try_into().unwrap()` or `::try_from(slice).unwrap()`. -/// -/// Array's `try_from(slice)` implementations (and the corresponding `slice.try_into()` -/// array implementations) succeed if the input slice length is the same as the result -/// array length. They optimize especially well when the optimizer can easily determine -/// the slice length, e.g. `<[u8; 4]>::try_from(&slice[4..8]).unwrap()`. Array implements -/// [TryFrom](crate::convert::TryFrom) returning: -/// -/// - `[T; N]` copies from the slice's elements -/// - `&[T; N]` references the original slice's elements -/// - `&mut [T; N]` references the original slice's elements -/// -/// You can move elements out of an array with a [slice pattern]. If you want -/// one element, see [`mem::replace`]. -/// -/// # Examples -/// -/// ``` -/// let mut array: [i32; 3] = [0; 3]; -/// -/// array[1] = 1; -/// array[2] = 2; -/// -/// assert_eq!([1, 2], &array[1..]); -/// -/// // This loop prints: 0 1 2 -/// for x in array { -/// print!("{x} "); -/// } -/// ``` -/// -/// You can also iterate over reference to the array's elements: -/// -/// ``` -/// let array: [i32; 3] = [0; 3]; -/// -/// for x in &array { } -/// ``` -/// -/// You can use `::try_from(slice)` or `slice.try_into()` to get an array from -/// a slice: -/// -/// ``` -/// let bytes: [u8; 3] = [1, 0, 2]; -/// assert_eq!(1, u16::from_le_bytes(<[u8; 2]>::try_from(&bytes[0..2]).unwrap())); -/// assert_eq!(512, u16::from_le_bytes(bytes[1..3].try_into().unwrap())); -/// ``` -/// -/// You can use a [slice pattern] to move elements out of an array: -/// -/// ``` -/// fn move_away(_: String) { /* Do interesting things. */ } -/// -/// let [john, roa] = ["John".to_string(), "Roa".to_string()]; -/// move_away(john); -/// move_away(roa); -/// ``` -/// -/// Arrays can be created from homogeneous tuples of appropriate length: -/// -/// ``` -/// let tuple: (u32, u32, u32) = (1, 2, 3); -/// let array: [u32; 3] = tuple.into(); -/// ``` -/// -/// # Editions -/// -/// Prior to Rust 1.53, arrays did not implement [`IntoIterator`] by value, so the method call -/// `array.into_iter()` auto-referenced into a [slice iterator](slice::iter). Right now, the old -/// behavior is preserved in the 2015 and 2018 editions of Rust for compatibility, ignoring -/// [`IntoIterator`] by value. In the future, the behavior on the 2015 and 2018 edition -/// might be made consistent to the behavior of later editions. -/// -/// ```rust,edition2018 -/// // Rust 2015 and 2018: -/// -/// # #![allow(array_into_iter)] // override our `deny(warnings)` -/// let array: [i32; 3] = [0; 3]; -/// -/// // This creates a slice iterator, producing references to each value. -/// for item in array.into_iter().enumerate() { -/// let (i, x): (usize, &i32) = item; -/// println!("array[{i}] = {x}"); -/// } -/// -/// // The `array_into_iter` lint suggests this change for future compatibility: -/// for item in array.iter().enumerate() { -/// let (i, x): (usize, &i32) = item; -/// println!("array[{i}] = {x}"); -/// } -/// -/// // You can explicitly iterate an array by value using `IntoIterator::into_iter` -/// for item in IntoIterator::into_iter(array).enumerate() { -/// let (i, x): (usize, i32) = item; -/// println!("array[{i}] = {x}"); -/// } -/// ``` -/// -/// Starting in the 2021 edition, `array.into_iter()` uses `IntoIterator` normally to iterate -/// by value, and `iter()` should be used to iterate by reference like previous editions. -/// -/// ```rust,edition2021 -/// // Rust 2021: -/// -/// let array: [i32; 3] = [0; 3]; -/// -/// // This iterates by reference: -/// for item in array.iter().enumerate() { -/// let (i, x): (usize, &i32) = item; -/// println!("array[{i}] = {x}"); -/// } -/// -/// // This iterates by value: -/// for item in array.into_iter().enumerate() { -/// let (i, x): (usize, i32) = item; -/// println!("array[{i}] = {x}"); -/// } -/// ``` -/// -/// Future language versions might start treating the `array.into_iter()` -/// syntax on editions 2015 and 2018 the same as on edition 2021. So code using -/// those older editions should still be written with this change in mind, to -/// prevent breakage in the future. The safest way to accomplish this is to -/// avoid the `into_iter` syntax on those editions. If an edition update is not -/// viable/desired, there are multiple alternatives: -/// * use `iter`, equivalent to the old behavior, creating references -/// * use [`IntoIterator::into_iter`], equivalent to the post-2021 behavior (Rust 1.53+) -/// * replace `for ... in array.into_iter() {` with `for ... in array {`, -/// equivalent to the post-2021 behavior (Rust 1.53+) -/// -/// ```rust,edition2018 -/// // Rust 2015 and 2018: -/// -/// let array: [i32; 3] = [0; 3]; -/// -/// // This iterates by reference: -/// for item in array.iter() { -/// let x: &i32 = item; -/// println!("{x}"); -/// } -/// -/// // This iterates by value: -/// for item in IntoIterator::into_iter(array) { -/// let x: i32 = item; -/// println!("{x}"); -/// } -/// -/// // This iterates by value: -/// for item in array { -/// let x: i32 = item; -/// println!("{x}"); -/// } -/// -/// // IntoIter can also start a chain. -/// // This iterates by value: -/// for item in IntoIterator::into_iter(array).enumerate() { -/// let (i, x): (usize, i32) = item; -/// println!("array[{i}] = {x}"); -/// } -/// ``` -/// -/// [slice]: prim@slice -/// [`Debug`]: fmt::Debug -/// [`Hash`]: hash::Hash -/// [`Borrow`]: borrow::Borrow -/// [`BorrowMut`]: borrow::BorrowMut -/// [slice pattern]: ../reference/patterns.html#slice-patterns -/// [`From`]: convert::From -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_array {} - -#[rustc_doc_primitive = "slice"] -#[doc(alias = "[")] -#[doc(alias = "]")] -#[doc(alias = "[]")] -/// A dynamically-sized view into a contiguous sequence, `[T]`. Contiguous here -/// means that elements are laid out so that every element is the same -/// distance from its neighbors. -/// -/// *[See also the `std::slice` module](crate::slice).* -/// -/// Slices are a view into a block of memory represented as a pointer and a -/// length. -/// -/// ``` -/// // slicing a Vec -/// let vec = vec![1, 2, 3]; -/// let int_slice = &vec[..]; -/// // coercing an array to a slice -/// let str_slice: &[&str] = &["one", "two", "three"]; -/// ``` -/// -/// Slices are either mutable or shared. The shared slice type is `&[T]`, -/// while the mutable slice type is `&mut [T]`, where `T` represents the element -/// type. For example, you can mutate the block of memory that a mutable slice -/// points to: -/// -/// ``` -/// let mut x = [1, 2, 3]; -/// let x = &mut x[..]; // Take a full slice of `x`. -/// x[1] = 7; -/// assert_eq!(x, &[1, 7, 3]); -/// ``` -/// -/// As slices store the length of the sequence they refer to, they have twice -/// the size of pointers to [`Sized`](marker/trait.Sized.html) types. -/// Also see the reference on -/// [dynamically sized types](../reference/dynamically-sized-types.html). -/// -/// ``` -/// # use std::rc::Rc; -/// let pointer_size = std::mem::size_of::<&u8>(); -/// assert_eq!(2 * pointer_size, std::mem::size_of::<&[u8]>()); -/// assert_eq!(2 * pointer_size, std::mem::size_of::<*const [u8]>()); -/// assert_eq!(2 * pointer_size, std::mem::size_of::>()); -/// assert_eq!(2 * pointer_size, std::mem::size_of::>()); -/// ``` -/// -/// ## Trait Implementations -/// -/// Some traits are implemented for slices if the element type implements -/// that trait. This includes [`Eq`], [`Hash`] and [`Ord`]. -/// -/// ## Iteration -/// -/// The slices implement `IntoIterator`. The iterator yields references to the -/// slice elements. -/// -/// ``` -/// let numbers: &[i32] = &[0, 1, 2]; -/// for n in numbers { -/// println!("{n} is a number!"); -/// } -/// ``` -/// -/// The mutable slice yields mutable references to the elements: -/// -/// ``` -/// let mut scores: &mut [i32] = &mut [7, 8, 9]; -/// for score in scores { -/// *score += 1; -/// } -/// ``` -/// -/// This iterator yields mutable references to the slice's elements, so while -/// the element type of the slice is `i32`, the element type of the iterator is -/// `&mut i32`. -/// -/// * [`.iter`] and [`.iter_mut`] are the explicit methods to return the default -/// iterators. -/// * Further methods that return iterators are [`.split`], [`.splitn`], -/// [`.chunks`], [`.windows`] and more. -/// -/// [`Hash`]: core::hash::Hash -/// [`.iter`]: slice::iter -/// [`.iter_mut`]: slice::iter_mut -/// [`.split`]: slice::split -/// [`.splitn`]: slice::splitn -/// [`.chunks`]: slice::chunks -/// [`.windows`]: slice::windows -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_slice {} - -#[rustc_doc_primitive = "str"] -/// String slices. -/// -/// *[See also the `std::str` module](crate::str).* -/// -/// The `str` type, also called a 'string slice', is the most primitive string -/// type. It is usually seen in its borrowed form, `&str`. It is also the type -/// of string literals, `&'static str`. -/// -/// String slices are always valid UTF-8. -/// -/// # Basic Usage -/// -/// String literals are string slices: -/// -/// ``` -/// let hello_world = "Hello, World!"; -/// ``` -/// -/// Here we have declared a string slice initialized with a string literal. -/// String literals have a static lifetime, which means the string `hello_world` -/// is guaranteed to be valid for the duration of the entire program. -/// We can explicitly specify `hello_world`'s lifetime as well: -/// -/// ``` -/// let hello_world: &'static str = "Hello, world!"; -/// ``` -/// -/// # Representation -/// -/// A `&str` is made up of two components: a pointer to some bytes, and a -/// length. You can look at these with the [`as_ptr`] and [`len`] methods: -/// -/// ``` -/// use std::slice; -/// use std::str; -/// -/// let story = "Once upon a time..."; -/// -/// let ptr = story.as_ptr(); -/// let len = story.len(); -/// -/// // story has nineteen bytes -/// assert_eq!(19, len); -/// -/// // We can re-build a str out of ptr and len. This is all unsafe because -/// // we are responsible for making sure the two components are valid: -/// let s = unsafe { -/// // First, we build a &[u8]... -/// let slice = slice::from_raw_parts(ptr, len); -/// -/// // ... and then convert that slice into a string slice -/// str::from_utf8(slice) -/// }; -/// -/// assert_eq!(s, Ok(story)); -/// ``` -/// -/// [`as_ptr`]: str::as_ptr -/// [`len`]: str::len -/// -/// Note: This example shows the internals of `&str`. `unsafe` should not be -/// used to get a string slice under normal circumstances. Use `as_str` -/// instead. -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_str {} - -#[rustc_doc_primitive = "tuple"] -#[doc(alias = "(")] -#[doc(alias = ")")] -#[doc(alias = "()")] -// -/// A finite heterogeneous sequence, `(T, U, ..)`. -/// -/// Let's cover each of those in turn: -/// -/// Tuples are *finite*. In other words, a tuple has a length. Here's a tuple -/// of length `3`: -/// -/// ``` -/// ("hello", 5, 'c'); -/// ``` -/// -/// 'Length' is also sometimes called 'arity' here; each tuple of a different -/// length is a different, distinct type. -/// -/// Tuples are *heterogeneous*. This means that each element of the tuple can -/// have a different type. In that tuple above, it has the type: -/// -/// ``` -/// # let _: -/// (&'static str, i32, char) -/// # = ("hello", 5, 'c'); -/// ``` -/// -/// Tuples are a *sequence*. This means that they can be accessed by position; -/// this is called 'tuple indexing', and it looks like this: -/// -/// ```rust -/// let tuple = ("hello", 5, 'c'); -/// -/// assert_eq!(tuple.0, "hello"); -/// assert_eq!(tuple.1, 5); -/// assert_eq!(tuple.2, 'c'); -/// ``` -/// -/// The sequential nature of the tuple applies to its implementations of various -/// traits. For example, in [`PartialOrd`] and [`Ord`], the elements are compared -/// sequentially until the first non-equal set is found. -/// -/// For more about tuples, see [the book](../book/ch03-02-data-types.html#the-tuple-type). -/// -// Hardcoded anchor in src/librustdoc/html/format.rs -// linked to as `#trait-implementations-1` -/// # Trait implementations -/// -/// In this documentation the shorthand `(T₁, T₂, …, Tₙ)` is used to represent tuples of varying -/// length. When that is used, any trait bound expressed on `T` applies to each element of the -/// tuple independently. Note that this is a convenience notation to avoid repetitive -/// documentation, not valid Rust syntax. -/// -/// Due to a temporary restriction in Rust’s type system, the following traits are only -/// implemented on tuples of arity 12 or less. In the future, this may change: -/// -/// * [`PartialEq`] -/// * [`Eq`] -/// * [`PartialOrd`] -/// * [`Ord`] -/// * [`Debug`] -/// * [`Default`] -/// * [`Hash`] -/// * [`From<[T; N]>`][from] -/// -/// [from]: convert::From -/// [`Debug`]: fmt::Debug -/// [`Hash`]: hash::Hash -/// -/// The following traits are implemented for tuples of any length. These traits have -/// implementations that are automatically generated by the compiler, so are not limited by -/// missing language features. -/// -/// * [`Clone`] -/// * [`Copy`] -/// * [`Send`] -/// * [`Sync`] -/// * [`Unpin`] -/// * [`UnwindSafe`] -/// * [`RefUnwindSafe`] -/// -/// [`UnwindSafe`]: panic::UnwindSafe -/// [`RefUnwindSafe`]: panic::RefUnwindSafe -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// let tuple = ("hello", 5, 'c'); -/// -/// assert_eq!(tuple.0, "hello"); -/// ``` -/// -/// Tuples are often used as a return type when you want to return more than -/// one value: -/// -/// ``` -/// fn calculate_point() -> (i32, i32) { -/// // Don't do a calculation, that's not the point of the example -/// (4, 5) -/// } -/// -/// let point = calculate_point(); -/// -/// assert_eq!(point.0, 4); -/// assert_eq!(point.1, 5); -/// -/// // Combining this with patterns can be nicer. -/// -/// let (x, y) = calculate_point(); -/// -/// assert_eq!(x, 4); -/// assert_eq!(y, 5); -/// ``` -/// -/// Homogeneous tuples can be created from arrays of appropriate length: -/// -/// ``` -/// let array: [u32; 3] = [1, 2, 3]; -/// let tuple: (u32, u32, u32) = array.into(); -/// ``` -/// -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_tuple {} - -// Required to make auto trait impls render. -// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls -#[doc(hidden)] -impl (T,) {} - -// Fake impl that's only really used for docs. -#[cfg(doc)] -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(fake_variadic)] -/// This trait is implemented on arbitrary-length tuples. -impl Clone for (T,) { - fn clone(&self) -> Self { - loop {} - } -} - -// Fake impl that's only really used for docs. -#[cfg(doc)] -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(fake_variadic)] -/// This trait is implemented on arbitrary-length tuples. -impl Copy for (T,) { - // empty -} - -#[rustc_doc_primitive = "f32"] -/// A 32-bit floating point type (specifically, the "binary32" type defined in IEEE 754-2008). -/// -/// This type can represent a wide range of decimal numbers, like `3.5`, `27`, -/// `-113.75`, `0.0078125`, `34359738368`, `0`, `-1`. So unlike integer types -/// (such as `i32`), floating point types can represent non-integer numbers, -/// too. -/// -/// However, being able to represent this wide range of numbers comes at the -/// cost of precision: floats can only represent some of the real numbers and -/// calculation with floats round to a nearby representable number. For example, -/// `5.0` and `1.0` can be exactly represented as `f32`, but `1.0 / 5.0` results -/// in `0.20000000298023223876953125` since `0.2` cannot be exactly represented -/// as `f32`. Note, however, that printing floats with `println` and friends will -/// often discard insignificant digits: `println!("{}", 1.0f32 / 5.0f32)` will -/// print `0.2`. -/// -/// Additionally, `f32` can represent some special values: -/// -/// - −0.0: IEEE 754 floating point numbers have a bit that indicates their sign, so −0.0 is a -/// possible value. For comparison −0.0 = +0.0, but floating point operations can carry -/// the sign bit through arithmetic operations. This means −0.0 × +0.0 produces −0.0 and -/// a negative number rounded to a value smaller than a float can represent also produces −0.0. -/// - [∞](#associatedconstant.INFINITY) and -/// [−∞](#associatedconstant.NEG_INFINITY): these result from calculations -/// like `1.0 / 0.0`. -/// - [NaN (not a number)](#associatedconstant.NAN): this value results from -/// calculations like `(-1.0).sqrt()`. NaN has some potentially unexpected -/// behavior: -/// - It is not equal to any float, including itself! This is the reason `f32` -/// doesn't implement the `Eq` trait. -/// - It is also neither smaller nor greater than any float, making it -/// impossible to sort by the default comparison operation, which is the -/// reason `f32` doesn't implement the `Ord` trait. -/// - It is also considered *infectious* as almost all calculations where one -/// of the operands is NaN will also result in NaN. The explanations on this -/// page only explicitly document behavior on NaN operands if this default -/// is deviated from. -/// - Lastly, there are multiple bit patterns that are considered NaN. -/// Rust does not currently guarantee that the bit patterns of NaN are -/// preserved over arithmetic operations, and they are not guaranteed to be -/// portable or even fully deterministic! This means that there may be some -/// surprising results upon inspecting the bit patterns, -/// as the same calculations might produce NaNs with different bit patterns. -/// -/// When the number resulting from a primitive operation (addition, -/// subtraction, multiplication, or division) on this type is not exactly -/// representable as `f32`, it is rounded according to the roundTiesToEven -/// direction defined in IEEE 754-2008. That means: -/// -/// - The result is the representable value closest to the true value, if there -/// is a unique closest representable value. -/// - If the true value is exactly half-way between two representable values, -/// the result is the one with an even least-significant binary digit. -/// - If the true value's magnitude is ≥ `f32::MAX` + 2(`f32::MAX_EXP` − -/// `f32::MANTISSA_DIGITS` − 1), the result is ∞ or −∞ (preserving the -/// true value's sign). -/// -/// For more information on floating point numbers, see [Wikipedia][wikipedia]. -/// -/// *[See also the `std::f32::consts` module](crate::f32::consts).* -/// -/// [wikipedia]: https://en.wikipedia.org/wiki/Single-precision_floating-point_format -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_f32 {} - -#[rustc_doc_primitive = "f64"] -/// A 64-bit floating point type (specifically, the "binary64" type defined in IEEE 754-2008). -/// -/// This type is very similar to [`f32`], but has increased -/// precision by using twice as many bits. Please see [the documentation for -/// `f32`][`f32`] or [Wikipedia on double precision -/// values][wikipedia] for more information. -/// -/// *[See also the `std::f64::consts` module](crate::f64::consts).* -/// -/// [`f32`]: prim@f32 -/// [wikipedia]: https://en.wikipedia.org/wiki/Double-precision_floating-point_format -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_f64 {} - -#[rustc_doc_primitive = "i8"] -// -/// The 8-bit signed integer type. -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_i8 {} - -#[rustc_doc_primitive = "i16"] -// -/// The 16-bit signed integer type. -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_i16 {} - -#[rustc_doc_primitive = "i32"] -// -/// The 32-bit signed integer type. -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_i32 {} - -#[rustc_doc_primitive = "i64"] -// -/// The 64-bit signed integer type. -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_i64 {} - -#[rustc_doc_primitive = "i128"] -// -/// The 128-bit signed integer type. -#[stable(feature = "i128", since = "1.26.0")] -mod prim_i128 {} - -#[rustc_doc_primitive = "u8"] -// -/// The 8-bit unsigned integer type. -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_u8 {} - -#[rustc_doc_primitive = "u16"] -// -/// The 16-bit unsigned integer type. -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_u16 {} - -#[rustc_doc_primitive = "u32"] -// -/// The 32-bit unsigned integer type. -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_u32 {} - -#[rustc_doc_primitive = "u64"] -// -/// The 64-bit unsigned integer type. -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_u64 {} - -#[rustc_doc_primitive = "u128"] -// -/// The 128-bit unsigned integer type. -#[stable(feature = "i128", since = "1.26.0")] -mod prim_u128 {} - -#[rustc_doc_primitive = "isize"] -// -/// The pointer-sized signed integer type. -/// -/// The size of this primitive is how many bytes it takes to reference any -/// location in memory. For example, on a 32 bit target, this is 4 bytes -/// and on a 64 bit target, this is 8 bytes. -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_isize {} - -#[rustc_doc_primitive = "usize"] -// -/// The pointer-sized unsigned integer type. -/// -/// The size of this primitive is how many bytes it takes to reference any -/// location in memory. For example, on a 32 bit target, this is 4 bytes -/// and on a 64 bit target, this is 8 bytes. -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_usize {} - -#[rustc_doc_primitive = "reference"] -#[doc(alias = "&")] -#[doc(alias = "&mut")] -// -/// References, `&T` and `&mut T`. -/// -/// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut` -/// operators on a value, or by using a [`ref`](../std/keyword.ref.html) or -/// [ref](../std/keyword.ref.html) [mut](../std/keyword.mut.html) pattern. -/// -/// For those familiar with pointers, a reference is just a pointer that is assumed to be -/// aligned, not null, and pointing to memory containing a valid value of `T` - for example, -/// &[bool] can only point to an allocation containing the integer values `1` -/// ([`true`](../std/keyword.true.html)) or `0` ([`false`](../std/keyword.false.html)), but -/// creating a &[bool] that points to an allocation containing -/// the value `3` causes undefined behaviour. -/// In fact, [Option]\<&T> has the same memory representation as a -/// nullable but aligned pointer, and can be passed across FFI boundaries as such. -/// -/// In most cases, references can be used much like the original value. Field access, method -/// calling, and indexing work the same (save for mutability rules, of course). In addition, the -/// comparison operators transparently defer to the referent's implementation, allowing references -/// to be compared the same as owned values. -/// -/// References have a lifetime attached to them, which represents the scope for which the borrow is -/// valid. A lifetime is said to "outlive" another one if its representative scope is as long or -/// longer than the other. The `'static` lifetime is the longest lifetime, which represents the -/// total life of the program. For example, string literals have a `'static` lifetime because the -/// text data is embedded into the binary of the program, rather than in an allocation that needs -/// to be dynamically managed. -/// -/// `&mut T` references can be freely coerced into `&T` references with the same referent type, and -/// references with longer lifetimes can be freely coerced into references with shorter ones. -/// -/// Reference equality by address, instead of comparing the values pointed to, is accomplished via -/// implicit reference-pointer coercion and raw pointer equality via [`ptr::eq`], while -/// [`PartialEq`] compares values. -/// -/// ``` -/// use std::ptr; -/// -/// let five = 5; -/// let other_five = 5; -/// let five_ref = &five; -/// let same_five_ref = &five; -/// let other_five_ref = &other_five; -/// -/// assert!(five_ref == same_five_ref); -/// assert!(five_ref == other_five_ref); -/// -/// assert!(ptr::eq(five_ref, same_five_ref)); -/// assert!(!ptr::eq(five_ref, other_five_ref)); -/// ``` -/// -/// For more information on how to use references, see [the book's section on "References and -/// Borrowing"][book-refs]. -/// -/// [book-refs]: ../book/ch04-02-references-and-borrowing.html -/// -/// # Trait implementations -/// -/// The following traits are implemented for all `&T`, regardless of the type of its referent: -/// -/// * [`Copy`] -/// * [`Clone`] \(Note that this will not defer to `T`'s `Clone` implementation if it exists!) -/// * [`Deref`] -/// * [`Borrow`] -/// * [`fmt::Pointer`] -/// -/// [`Deref`]: ops::Deref -/// [`Borrow`]: borrow::Borrow -/// -/// `&mut T` references get all of the above except `Copy` and `Clone` (to prevent creating -/// multiple simultaneous mutable borrows), plus the following, regardless of the type of its -/// referent: -/// -/// * [`DerefMut`] -/// * [`BorrowMut`] -/// -/// [`DerefMut`]: ops::DerefMut -/// [`BorrowMut`]: borrow::BorrowMut -/// [bool]: prim@bool -/// -/// The following traits are implemented on `&T` references if the underlying `T` also implements -/// that trait: -/// -/// * All the traits in [`std::fmt`] except [`fmt::Pointer`] (which is implemented regardless of the type of its referent) and [`fmt::Write`] -/// * [`PartialOrd`] -/// * [`Ord`] -/// * [`PartialEq`] -/// * [`Eq`] -/// * [`AsRef`] -/// * [`Fn`] \(in addition, `&T` references get [`FnMut`] and [`FnOnce`] if `T: Fn`) -/// * [`Hash`] -/// * [`ToSocketAddrs`] -/// * [`Send`] \(`&T` references also require T: [Sync]) -/// * [`Sync`] -/// -/// [`std::fmt`]: fmt -/// [`Hash`]: hash::Hash -#[doc = concat!("[`ToSocketAddrs`]: ", include_str!("../primitive_docs/net_tosocketaddrs.md"))] -/// -/// `&mut T` references get all of the above except `ToSocketAddrs`, plus the following, if `T` -/// implements that trait: -/// -/// * [`AsMut`] -/// * [`FnMut`] \(in addition, `&mut T` references get [`FnOnce`] if `T: FnMut`) -/// * [`fmt::Write`] -/// * [`Iterator`] -/// * [`DoubleEndedIterator`] -/// * [`ExactSizeIterator`] -/// * [`FusedIterator`] -/// * [`TrustedLen`] -/// * [`io::Write`] -/// * [`Read`] -/// * [`Seek`] -/// * [`BufRead`] -/// -/// [`FusedIterator`]: iter::FusedIterator -/// [`TrustedLen`]: iter::TrustedLen -#[doc = concat!("[`Seek`]: ", include_str!("../primitive_docs/io_seek.md"))] -#[doc = concat!("[`BufRead`]: ", include_str!("../primitive_docs/io_bufread.md"))] -#[doc = concat!("[`Read`]: ", include_str!("../primitive_docs/io_read.md"))] -#[doc = concat!("[`io::Write`]: ", include_str!("../primitive_docs/io_write.md"))] -/// -/// Note that due to method call deref coercion, simply calling a trait method will act like they -/// work on references as well as they do on owned values! The implementations described here are -/// meant for generic contexts, where the final type `T` is a type parameter or otherwise not -/// locally known. -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_ref {} - -#[rustc_doc_primitive = "fn"] -// -/// Function pointers, like `fn(usize) -> bool`. -/// -/// *See also the traits [`Fn`], [`FnMut`], and [`FnOnce`].* -/// -/// Function pointers are pointers that point to *code*, not data. They can be called -/// just like functions. Like references, function pointers are, among other things, assumed to -/// not be null, so if you want to pass a function pointer over FFI and be able to accommodate null -/// pointers, make your type [`Option`](core::option#options-and-pointers-nullable-pointers) -/// with your required signature. -/// -/// ### Safety -/// -/// Plain function pointers are obtained by casting either plain functions, or closures that don't -/// capture an environment: -/// -/// ``` -/// fn add_one(x: usize) -> usize { -/// x + 1 -/// } -/// -/// let ptr: fn(usize) -> usize = add_one; -/// assert_eq!(ptr(5), 6); -/// -/// let clos: fn(usize) -> usize = |x| x + 5; -/// assert_eq!(clos(5), 10); -/// ``` -/// -/// In addition to varying based on their signature, function pointers come in two flavors: safe -/// and unsafe. Plain `fn()` function pointers can only point to safe functions, -/// while `unsafe fn()` function pointers can point to safe or unsafe functions. -/// -/// ``` -/// fn add_one(x: usize) -> usize { -/// x + 1 -/// } -/// -/// unsafe fn add_one_unsafely(x: usize) -> usize { -/// x + 1 -/// } -/// -/// let safe_ptr: fn(usize) -> usize = add_one; -/// -/// //ERROR: mismatched types: expected normal fn, found unsafe fn -/// //let bad_ptr: fn(usize) -> usize = add_one_unsafely; -/// -/// let unsafe_ptr: unsafe fn(usize) -> usize = add_one_unsafely; -/// let really_safe_ptr: unsafe fn(usize) -> usize = add_one; -/// ``` -/// -/// ### ABI -/// -/// On top of that, function pointers can vary based on what ABI they use. This -/// is achieved by adding the `extern` keyword before the type, followed by the -/// ABI in question. The default ABI is "Rust", i.e., `fn()` is the exact same -/// type as `extern "Rust" fn()`. A pointer to a function with C ABI would have -/// type `extern "C" fn()`. -/// -/// `extern "ABI" { ... }` blocks declare functions with ABI "ABI". The default -/// here is "C", i.e., functions declared in an `extern {...}` block have "C" -/// ABI. -/// -/// For more information and a list of supported ABIs, see [the nomicon's -/// section on foreign calling conventions][nomicon-abi]. -/// -/// [nomicon-abi]: ../nomicon/ffi.html#foreign-calling-conventions -/// -/// ### Variadic functions -/// -/// Extern function declarations with the "C" or "cdecl" ABIs can also be *variadic*, allowing them -/// to be called with a variable number of arguments. Normal Rust functions, even those with an -/// `extern "ABI"`, cannot be variadic. For more information, see [the nomicon's section on -/// variadic functions][nomicon-variadic]. -/// -/// [nomicon-variadic]: ../nomicon/ffi.html#variadic-functions -/// -/// ### Creating function pointers -/// -/// When `bar` is the name of a function, then the expression `bar` is *not* a -/// function pointer. Rather, it denotes a value of an unnameable type that -/// uniquely identifies the function `bar`. The value is zero-sized because the -/// type already identifies the function. This has the advantage that "calling" -/// the value (it implements the `Fn*` traits) does not require dynamic -/// dispatch. -/// -/// This zero-sized type *coerces* to a regular function pointer. For example: -/// -/// ```rust -/// use std::mem; -/// -/// fn bar(x: i32) {} -/// -/// let not_bar_ptr = bar; // `not_bar_ptr` is zero-sized, uniquely identifying `bar` -/// assert_eq!(mem::size_of_val(¬_bar_ptr), 0); -/// -/// let bar_ptr: fn(i32) = not_bar_ptr; // force coercion to function pointer -/// assert_eq!(mem::size_of_val(&bar_ptr), mem::size_of::()); -/// -/// let footgun = &bar; // this is a shared reference to the zero-sized type identifying `bar` -/// ``` -/// -/// The last line shows that `&bar` is not a function pointer either. Rather, it -/// is a reference to the function-specific ZST. `&bar` is basically never what you -/// want when `bar` is a function. -/// -/// ### Casting to and from integers -/// -/// You cast function pointers directly to integers: -/// -/// ```rust -/// let fnptr: fn(i32) -> i32 = |x| x+2; -/// let fnptr_addr = fnptr as usize; -/// ``` -/// -/// However, a direct cast back is not possible. You need to use `transmute`: -/// -/// ```rust -/// # #[cfg(not(miri))] { // FIXME: use strict provenance APIs once they are stable, then remove this `cfg` -/// # let fnptr: fn(i32) -> i32 = |x| x+2; -/// # let fnptr_addr = fnptr as usize; -/// let fnptr = fnptr_addr as *const (); -/// let fnptr: fn(i32) -> i32 = unsafe { std::mem::transmute(fnptr) }; -/// assert_eq!(fnptr(40), 42); -/// # } -/// ``` -/// -/// Crucially, we `as`-cast to a raw pointer before `transmute`ing to a function pointer. -/// This avoids an integer-to-pointer `transmute`, which can be problematic. -/// Transmuting between raw pointers and function pointers (i.e., two pointer types) is fine. -/// -/// Note that all of this is not portable to platforms where function pointers and data pointers -/// have different sizes. -/// -/// ### Trait implementations -/// -/// In this documentation the shorthand `fn (T₁, T₂, …, Tₙ)` is used to represent non-variadic -/// function pointers of varying length. Note that this is a convenience notation to avoid -/// repetitive documentation, not valid Rust syntax. -/// -/// Due to a temporary restriction in Rust's type system, these traits are only implemented on -/// functions that take 12 arguments or less, with the `"Rust"` and `"C"` ABIs. In the future, this -/// may change: -/// -/// * [`PartialEq`] -/// * [`Eq`] -/// * [`PartialOrd`] -/// * [`Ord`] -/// * [`Hash`] -/// * [`Pointer`] -/// * [`Debug`] -/// -/// The following traits are implemented for function pointers with any number of arguments and -/// any ABI. These traits have implementations that are automatically generated by the compiler, -/// so are not limited by missing language features: -/// -/// * [`Clone`] -/// * [`Copy`] -/// * [`Send`] -/// * [`Sync`] -/// * [`Unpin`] -/// * [`UnwindSafe`] -/// * [`RefUnwindSafe`] -/// -/// [`Hash`]: hash::Hash -/// [`Pointer`]: fmt::Pointer -/// [`UnwindSafe`]: panic::UnwindSafe -/// [`RefUnwindSafe`]: panic::RefUnwindSafe -/// -/// In addition, all *safe* function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`], because -/// these traits are specially known to the compiler. -#[stable(feature = "rust1", since = "1.0.0")] -mod prim_fn {} - -// Required to make auto trait impls render. -// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls -#[doc(hidden)] -impl fn(T) -> Ret {} - -// Fake impl that's only really used for docs. -#[cfg(doc)] -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(fake_variadic)] -/// This trait is implemented on function pointers with any number of arguments. -impl Clone for fn(T) -> Ret { - fn clone(&self) -> Self { - loop {} - } -} - -// Fake impl that's only really used for docs. -#[cfg(doc)] -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(fake_variadic)] -/// This trait is implemented on function pointers with any number of arguments. -impl Copy for fn(T) -> Ret { - // empty -} diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index ca160017202..fc69c143222 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -63,7 +63,6 @@ pub mod features; pub mod fluent_alphabetical; pub mod mir_opt_tests; pub mod pal; -pub mod primitive_docs; pub mod rustdoc_css_themes; pub mod rustdoc_gui_tests; pub mod style; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 5585e125f2d..80e58ba00fc 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -112,7 +112,6 @@ fn main() { // Checks that only make sense for the std libs. check!(pal, &library_path); - check!(primitive_docs, &library_path); // Checks that need to be done for both the compiler and std libraries. check!(unit_tests, &src_path); diff --git a/src/tools/tidy/src/primitive_docs.rs b/src/tools/tidy/src/primitive_docs.rs deleted file mode 100644 index f3200e0afd7..00000000000 --- a/src/tools/tidy/src/primitive_docs.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Tidy check to make sure `library/{std,core}/src/primitive_docs.rs` are the same file. These are -//! different files so that relative links work properly without having to have `CARGO_PKG_NAME` -//! set, but conceptually they should always be the same. - -use std::path::Path; - -pub fn check(library_path: &Path, bad: &mut bool) { - let std_name = "std/src/primitive_docs.rs"; - let core_name = "core/src/primitive_docs.rs"; - let std_contents = std::fs::read_to_string(library_path.join(std_name)) - .unwrap_or_else(|e| panic!("failed to read library/{std_name}: {e}")); - let core_contents = std::fs::read_to_string(library_path.join(core_name)) - .unwrap_or_else(|e| panic!("failed to read library/{core_name}: {e}")); - if std_contents != core_contents { - tidy_error!(bad, "library/{core_name} and library/{std_name} have different contents"); - } -} From 4690f97099c2f01a07a8fa272e94e3e5cba61f12 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 18 Sep 2023 21:11:14 +1000 Subject: [PATCH 227/250] coverage: Fix an unstable-sort inconsistency in coverage spans This code was calling `sort_unstable_by`, but failed to impose a total order on the initial spans. That resulted in unpredictable handling of closure spans, producing inconsistencies in the coverage maps and in user-visible coverage reports. This patch fixes the problem by always sorting closure spans before otherwise-identical non-closure spans, and also switches to a stable sort in case the ordering is still not total. --- compiler/rustc_mir_transform/src/coverage/spans.rs | 5 ++++- tests/coverage-map/status-quo/closure.cov-map | 14 +++++++------- tests/coverage-map/status-quo/generator.cov-map | 4 ++-- tests/run-coverage/closure.coverage | 14 +++++++------- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 717763a94a0..d254c1662e4 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -333,7 +333,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { initial_spans.push(CoverageSpan::for_fn_sig(self.fn_sig_span)); - initial_spans.sort_unstable_by(|a, b| { + initial_spans.sort_by(|a, b| { if a.span.lo() == b.span.lo() { if a.span.hi() == b.span.hi() { if a.is_in_same_bcb(b) { @@ -357,6 +357,9 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { a.span.lo().partial_cmp(&b.span.lo()) } .unwrap() + // If two spans are otherwise identical, put closure spans first, + // as this seems to be what the refinement step expects. + .then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse()) }); initial_spans diff --git a/tests/coverage-map/status-quo/closure.cov-map b/tests/coverage-map/status-quo/closure.cov-map index f3a32f091a9..7dbf6ec834d 100644 --- a/tests/coverage-map/status-quo/closure.cov-map +++ b/tests/coverage-map/status-quo/closure.cov-map @@ -1,5 +1,5 @@ Function name: closure::main -Raw bytes (170): 0x[01, 01, 17, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 05, 05, 5a, 01, 05, 18, 01, 08, 01, 0f, 0d, 03, 16, 0e, 06, 0a, 07, 10, 05, 13, 0d, 0b, 1a, 0e, 08, 09, 0f, 10, 05, 0e, 09, 13, 16, 05, 0d, 18, 17, 19, 09, 01, 21, 1b, 04, 09, 00, 29, 1f, 01, 09, 00, 2d, 23, 01, 09, 00, 24, 27, 05, 09, 00, 24, 2b, 02, 09, 00, 21, 2f, 04, 09, 00, 21, 33, 04, 09, 00, 28, 37, 09, 09, 00, 32, 3b, 04, 09, 00, 33, 3f, 07, 09, 00, 4b, 43, 08, 09, 01, 09, 47, 0a, 09, 01, 09, 4b, 08, 09, 01, 09, 4f, 0a, 08, 00, 10, 05, 00, 11, 04, 06, 5a, 04, 06, 00, 07, 57, 01, 05, 03, 02] +Raw bytes (170): 0x[01, 01, 17, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 00, 01, 05, 05, 5a, 01, 05, 18, 01, 08, 01, 0f, 0d, 03, 16, 0e, 06, 0a, 07, 10, 05, 13, 0d, 0b, 1a, 0e, 06, 0a, 0f, 10, 05, 0c, 16, 13, 16, 05, 0d, 18, 17, 19, 09, 01, 1e, 1b, 04, 09, 00, 29, 1f, 01, 09, 00, 2d, 23, 01, 09, 00, 24, 27, 05, 09, 00, 24, 2b, 02, 09, 00, 21, 2f, 04, 09, 00, 21, 33, 04, 09, 00, 28, 37, 09, 09, 00, 32, 3b, 04, 09, 00, 33, 3f, 07, 09, 00, 4b, 43, 08, 09, 00, 48, 47, 0a, 09, 00, 47, 4b, 08, 09, 00, 44, 4f, 0a, 08, 00, 10, 05, 00, 11, 04, 06, 5a, 04, 06, 00, 07, 57, 01, 05, 03, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 23 @@ -32,13 +32,13 @@ Number of file 0 mappings: 24 = (c0 + Zero) - Code(Expression(1, Add)) at (prev + 16, 5) to (start + 19, 13) = (c0 + Zero) -- Code(Expression(2, Add)) at (prev + 26, 14) to (start + 8, 9) +- Code(Expression(2, Add)) at (prev + 26, 14) to (start + 6, 10) = (c0 + Zero) -- Code(Expression(3, Add)) at (prev + 16, 5) to (start + 14, 9) +- Code(Expression(3, Add)) at (prev + 16, 5) to (start + 12, 22) = (c0 + Zero) - Code(Expression(4, Add)) at (prev + 22, 5) to (start + 13, 24) = (c0 + Zero) -- Code(Expression(5, Add)) at (prev + 25, 9) to (start + 1, 33) +- Code(Expression(5, Add)) at (prev + 25, 9) to (start + 1, 30) = (c0 + Zero) - Code(Expression(6, Add)) at (prev + 4, 9) to (start + 0, 41) = (c0 + Zero) @@ -60,11 +60,11 @@ Number of file 0 mappings: 24 = (c0 + Zero) - Code(Expression(15, Add)) at (prev + 7, 9) to (start + 0, 75) = (c0 + Zero) -- Code(Expression(16, Add)) at (prev + 8, 9) to (start + 1, 9) +- Code(Expression(16, Add)) at (prev + 8, 9) to (start + 0, 72) = (c0 + Zero) -- Code(Expression(17, Add)) at (prev + 10, 9) to (start + 1, 9) +- Code(Expression(17, Add)) at (prev + 10, 9) to (start + 0, 71) = (c0 + Zero) -- Code(Expression(18, Add)) at (prev + 8, 9) to (start + 1, 9) +- Code(Expression(18, Add)) at (prev + 8, 9) to (start + 0, 68) = (c0 + Zero) - Code(Expression(19, Add)) at (prev + 10, 8) to (start + 0, 16) = (c0 + Zero) diff --git a/tests/coverage-map/status-quo/generator.cov-map b/tests/coverage-map/status-quo/generator.cov-map index 6e10b58a941..a66c1af30f9 100644 --- a/tests/coverage-map/status-quo/generator.cov-map +++ b/tests/coverage-map/status-quo/generator.cov-map @@ -14,7 +14,7 @@ Number of file 0 mappings: 4 = (c1 + (c0 - c1)) Function name: generator::main -Raw bytes (71): 0x[01, 01, 0b, 01, 00, 05, 0b, 09, 0d, 11, 00, 11, 15, 2a, 19, 11, 15, 15, 19, 26, 00, 2a, 19, 11, 15, 09, 01, 0f, 01, 02, 19, 03, 07, 0b, 00, 2e, 11, 01, 2b, 00, 2d, 07, 01, 0e, 00, 35, 0f, 02, 0b, 00, 2e, 2a, 01, 22, 00, 27, 26, 00, 2c, 00, 2e, 1f, 01, 0e, 00, 35, 23, 02, 01, 00, 02] +Raw bytes (71): 0x[01, 01, 0b, 01, 00, 05, 0b, 09, 0d, 11, 00, 11, 15, 2a, 19, 11, 15, 15, 19, 26, 00, 2a, 19, 11, 15, 09, 01, 0f, 01, 02, 16, 03, 07, 0b, 00, 2e, 11, 01, 2b, 00, 2d, 07, 01, 0e, 00, 35, 0f, 02, 0b, 00, 2e, 2a, 01, 22, 00, 27, 26, 00, 2c, 00, 2e, 1f, 01, 0e, 00, 35, 23, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 11 @@ -30,7 +30,7 @@ Number of expressions: 11 - expression 9 operands: lhs = Expression(10, Sub), rhs = Counter(6) - expression 10 operands: lhs = Counter(4), rhs = Counter(5) Number of file 0 mappings: 9 -- Code(Counter(0)) at (prev + 15, 1) to (start + 2, 25) +- Code(Counter(0)) at (prev + 15, 1) to (start + 2, 22) - Code(Expression(0, Add)) at (prev + 7, 11) to (start + 0, 46) = (c0 + Zero) - Code(Counter(4)) at (prev + 1, 43) to (start + 0, 45) diff --git a/tests/run-coverage/closure.coverage b/tests/run-coverage/closure.coverage index 930348dc431..67014f792c8 100644 --- a/tests/run-coverage/closure.coverage +++ b/tests/run-coverage/closure.coverage @@ -76,8 +76,8 @@ LL| 1| some_string = None; LL| 1| let LL| 1| a - LL| 1| = - LL| 1| || + LL| | = + LL| | || LL| 1| { LL| 1| let mut countdown = 0; LL| 1| if is_false { @@ -98,8 +98,8 @@ LL| 1| LL| 1| let LL| 1| quote_closure - LL| 1| = - LL| 1| |val| + LL| | = + LL| | |val| LL| 5| { LL| 5| let mut countdown = 0; LL| 5| if is_false { @@ -186,7 +186,7 @@ LL| | ; LL| | LL| 1| let short_used_not_covered_closure_line_break_block_embedded_branch = - LL| 1| | _unused_arg: u8 | + LL| | | _unused_arg: u8 | LL| 0| { LL| 0| println!( LL| 0| "not called: {}", @@ -196,7 +196,7 @@ LL| | ; LL| | LL| 1| let short_used_covered_closure_line_break_no_block_embedded_branch = - LL| 1| | _unused_arg: u8 | + LL| | | _unused_arg: u8 | LL| 1| println!( LL| 1| "not called: {}", LL| 1| if is_true { "check" } else { "me" } @@ -205,7 +205,7 @@ LL| | ; LL| | LL| 1| let short_used_covered_closure_line_break_block_embedded_branch = - LL| 1| | _unused_arg: u8 | + LL| | | _unused_arg: u8 | LL| 1| { LL| 1| println!( LL| 1| "not called: {}", From ee451f8faccf3050c76cdcd82543c917b40c7962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 18 Sep 2023 13:45:42 +0200 Subject: [PATCH 228/250] Fix build on Windows --- src/tools/opt-dist/src/environment.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/opt-dist/src/environment.rs b/src/tools/opt-dist/src/environment.rs index f7b5c176375..ff782a1687e 100644 --- a/src/tools/opt-dist/src/environment.rs +++ b/src/tools/opt-dist/src/environment.rs @@ -18,6 +18,7 @@ pub struct Environment { /// List of test paths that should be skipped when testing the optimized artifacts. skipped_tests: Vec, /// Directory containing a pre-built rustc-perf checkout. + #[builder(default)] prebuilt_rustc_perf: Option, use_bolt: bool, shared_llvm: bool, From a4cb31bb58112b65b4313a2764addf5b72ff7a68 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 18 Sep 2023 22:19:16 +1000 Subject: [PATCH 229/250] coverage: Regression test for inconsistent handling of closure spans --- .../status-quo/closure_bug.cov-map | 148 ++++++++++++++++++ tests/coverage-map/status-quo/closure_bug.rs | 44 ++++++ tests/run-coverage/closure_bug.coverage | 53 +++++++ tests/run-coverage/closure_bug.rs | 44 ++++++ 4 files changed, 289 insertions(+) create mode 100644 tests/coverage-map/status-quo/closure_bug.cov-map create mode 100644 tests/coverage-map/status-quo/closure_bug.rs create mode 100644 tests/run-coverage/closure_bug.coverage create mode 100644 tests/run-coverage/closure_bug.rs diff --git a/tests/coverage-map/status-quo/closure_bug.cov-map b/tests/coverage-map/status-quo/closure_bug.cov-map new file mode 100644 index 00000000000..4fe2e5ad243 --- /dev/null +++ b/tests/coverage-map/status-quo/closure_bug.cov-map @@ -0,0 +1,148 @@ +Function name: closure_bug::main +Raw bytes (241): 0x[01, 01, 34, 01, 00, 01, 05, 05, ce, 01, 01, 05, cb, 01, 00, 05, ce, 01, 01, 05, cb, 01, 09, 05, ce, 01, 01, 05, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, c3, 01, 00, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, c3, 01, 0d, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, 0d, be, 01, c3, 01, 0d, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, bb, 01, 00, 0d, be, 01, c3, 01, 0d, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, bb, 01, 11, 0d, be, 01, c3, 01, 0d, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, 11, b6, 01, bb, 01, 11, 0d, be, 01, c3, 01, 0d, 09, c6, 01, cb, 01, 09, 05, ce, 01, 01, 05, 11, 01, 06, 01, 03, 0a, 03, 09, 05, 01, 0e, 05, 01, 0f, 00, 17, ce, 01, 00, 17, 00, 18, cb, 01, 02, 09, 00, 0a, 13, 06, 05, 01, 0e, 09, 01, 0f, 00, 17, c6, 01, 00, 17, 00, 18, c3, 01, 02, 09, 00, 0a, 3b, 06, 05, 01, 0e, 0d, 01, 0f, 00, 17, be, 01, 00, 17, 00, 18, bb, 01, 02, 09, 00, 0a, 7b, 06, 05, 01, 0e, 11, 01, 0f, 00, 17, b6, 01, 00, 17, 00, 18, b3, 01, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 52 +- expression 0 operands: lhs = Counter(0), rhs = Zero +- expression 1 operands: lhs = Counter(0), rhs = Counter(1) +- expression 2 operands: lhs = Counter(1), rhs = Expression(51, Sub) +- expression 3 operands: lhs = Counter(0), rhs = Counter(1) +- expression 4 operands: lhs = Expression(50, Add), rhs = Zero +- expression 5 operands: lhs = Counter(1), rhs = Expression(51, Sub) +- expression 6 operands: lhs = Counter(0), rhs = Counter(1) +- expression 7 operands: lhs = Expression(50, Add), rhs = Counter(2) +- expression 8 operands: lhs = Counter(1), rhs = Expression(51, Sub) +- expression 9 operands: lhs = Counter(0), rhs = Counter(1) +- expression 10 operands: lhs = Counter(2), rhs = Expression(49, Sub) +- expression 11 operands: lhs = Expression(50, Add), rhs = Counter(2) +- expression 12 operands: lhs = Counter(1), rhs = Expression(51, Sub) +- expression 13 operands: lhs = Counter(0), rhs = Counter(1) +- expression 14 operands: lhs = Expression(48, Add), rhs = Zero +- expression 15 operands: lhs = Counter(2), rhs = Expression(49, Sub) +- expression 16 operands: lhs = Expression(50, Add), rhs = Counter(2) +- expression 17 operands: lhs = Counter(1), rhs = Expression(51, Sub) +- expression 18 operands: lhs = Counter(0), rhs = Counter(1) +- expression 19 operands: lhs = Expression(48, Add), rhs = Counter(3) +- expression 20 operands: lhs = Counter(2), rhs = Expression(49, Sub) +- expression 21 operands: lhs = Expression(50, Add), rhs = Counter(2) +- expression 22 operands: lhs = Counter(1), rhs = Expression(51, Sub) +- expression 23 operands: lhs = Counter(0), rhs = Counter(1) +- expression 24 operands: lhs = Counter(3), rhs = Expression(47, Sub) +- expression 25 operands: lhs = Expression(48, Add), rhs = Counter(3) +- expression 26 operands: lhs = Counter(2), rhs = Expression(49, Sub) +- expression 27 operands: lhs = Expression(50, Add), rhs = Counter(2) +- expression 28 operands: lhs = Counter(1), rhs = Expression(51, Sub) +- expression 29 operands: lhs = Counter(0), rhs = Counter(1) +- expression 30 operands: lhs = Expression(46, Add), rhs = Zero +- expression 31 operands: lhs = Counter(3), rhs = Expression(47, Sub) +- expression 32 operands: lhs = Expression(48, Add), rhs = Counter(3) +- expression 33 operands: lhs = Counter(2), rhs = Expression(49, Sub) +- expression 34 operands: lhs = Expression(50, Add), rhs = Counter(2) +- expression 35 operands: lhs = Counter(1), rhs = Expression(51, Sub) +- expression 36 operands: lhs = Counter(0), rhs = Counter(1) +- expression 37 operands: lhs = Expression(46, Add), rhs = Counter(4) +- expression 38 operands: lhs = Counter(3), rhs = Expression(47, Sub) +- expression 39 operands: lhs = Expression(48, Add), rhs = Counter(3) +- expression 40 operands: lhs = Counter(2), rhs = Expression(49, Sub) +- expression 41 operands: lhs = Expression(50, Add), rhs = Counter(2) +- expression 42 operands: lhs = Counter(1), rhs = Expression(51, Sub) +- expression 43 operands: lhs = Counter(0), rhs = Counter(1) +- expression 44 operands: lhs = Counter(4), rhs = Expression(45, Sub) +- expression 45 operands: lhs = Expression(46, Add), rhs = Counter(4) +- expression 46 operands: lhs = Counter(3), rhs = Expression(47, Sub) +- expression 47 operands: lhs = Expression(48, Add), rhs = Counter(3) +- expression 48 operands: lhs = Counter(2), rhs = Expression(49, Sub) +- expression 49 operands: lhs = Expression(50, Add), rhs = Counter(2) +- expression 50 operands: lhs = Counter(1), rhs = Expression(51, Sub) +- expression 51 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 17 +- Code(Counter(0)) at (prev + 6, 1) to (start + 3, 10) +- Code(Expression(0, Add)) at (prev + 9, 5) to (start + 1, 14) + = (c0 + Zero) +- Code(Counter(1)) at (prev + 1, 15) to (start + 0, 23) +- Code(Expression(51, Sub)) at (prev + 0, 23) to (start + 0, 24) + = (c0 - c1) +- Code(Expression(50, Add)) at (prev + 2, 9) to (start + 0, 10) + = (c1 + (c0 - c1)) +- Code(Expression(4, Add)) at (prev + 6, 5) to (start + 1, 14) + = ((c1 + (c0 - c1)) + Zero) +- Code(Counter(2)) at (prev + 1, 15) to (start + 0, 23) +- Code(Expression(49, Sub)) at (prev + 0, 23) to (start + 0, 24) + = ((c1 + (c0 - c1)) - c2) +- Code(Expression(48, Add)) at (prev + 2, 9) to (start + 0, 10) + = (c2 + ((c1 + (c0 - c1)) - c2)) +- Code(Expression(14, Add)) at (prev + 6, 5) to (start + 1, 14) + = ((c2 + ((c1 + (c0 - c1)) - c2)) + Zero) +- Code(Counter(3)) at (prev + 1, 15) to (start + 0, 23) +- Code(Expression(47, Sub)) at (prev + 0, 23) to (start + 0, 24) + = ((c2 + ((c1 + (c0 - c1)) - c2)) - c3) +- Code(Expression(46, Add)) at (prev + 2, 9) to (start + 0, 10) + = (c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) +- Code(Expression(30, Add)) at (prev + 6, 5) to (start + 1, 14) + = ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) + Zero) +- Code(Counter(4)) at (prev + 1, 15) to (start + 0, 23) +- Code(Expression(45, Sub)) at (prev + 0, 23) to (start + 0, 24) + = ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4) +- Code(Expression(44, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) + +Function name: closure_bug::main::{closure#0} +Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 0d, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 07, 00, 29, 00, 2a] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 13, 9) to (start + 0, 18) +- Code(Counter(1)) at (prev + 0, 21) to (start + 0, 25) +- Code(Expression(0, Sub)) at (prev + 0, 35) to (start + 0, 40) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 0, 41) to (start + 0, 42) + = (c1 + (c0 - c1)) + +Function name: closure_bug::main::{closure#1} +Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 16, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 07, 00, 29, 00, 2a] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 22, 9) to (start + 0, 18) +- Code(Counter(1)) at (prev + 0, 21) to (start + 0, 25) +- Code(Expression(0, Sub)) at (prev + 0, 35) to (start + 0, 40) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 0, 41) to (start + 0, 42) + = (c1 + (c0 - c1)) + +Function name: closure_bug::main::{closure#2} +Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 1f, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 07, 00, 29, 00, 2a] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 31, 9) to (start + 0, 18) +- Code(Counter(1)) at (prev + 0, 21) to (start + 0, 25) +- Code(Expression(0, Sub)) at (prev + 0, 35) to (start + 0, 40) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 0, 41) to (start + 0, 42) + = (c1 + (c0 - c1)) + +Function name: closure_bug::main::{closure#3} +Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 28, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 07, 00, 29, 00, 2a] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 40, 9) to (start + 0, 18) +- Code(Counter(1)) at (prev + 0, 21) to (start + 0, 25) +- Code(Expression(0, Sub)) at (prev + 0, 35) to (start + 0, 40) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 0, 41) to (start + 0, 42) + = (c1 + (c0 - c1)) + diff --git a/tests/coverage-map/status-quo/closure_bug.rs b/tests/coverage-map/status-quo/closure_bug.rs new file mode 100644 index 00000000000..739bc5f0b51 --- /dev/null +++ b/tests/coverage-map/status-quo/closure_bug.rs @@ -0,0 +1,44 @@ +// Regression test for #115930. +// All of these closures are identical, and should produce identical output in +// the coverage report. However, an unstable sort was causing them to be treated +// inconsistently when preparing coverage spans. + +fn main() { + let truthy = std::env::args().len() == 1; + + let a + = + | + | + if truthy { true } else { false }; + + a(); + if truthy { a(); } + + let b + = + | + | + if truthy { true } else { false }; + + b(); + if truthy { b(); } + + let c + = + | + | + if truthy { true } else { false }; + + c(); + if truthy { c(); } + + let d + = + | + | + if truthy { true } else { false }; + + d(); + if truthy { d(); } +} diff --git a/tests/run-coverage/closure_bug.coverage b/tests/run-coverage/closure_bug.coverage new file mode 100644 index 00000000000..f3299834bce --- /dev/null +++ b/tests/run-coverage/closure_bug.coverage @@ -0,0 +1,53 @@ + LL| |// Regression test for #115930. + LL| |// All of these closures are identical, and should produce identical output in + LL| |// the coverage report. However, an unstable sort was causing them to be treated + LL| |// inconsistently when preparing coverage spans. + LL| | + LL| 1|fn main() { + LL| 1| let truthy = std::env::args().len() == 1; + LL| 1| + LL| 1| let a + LL| | = + LL| | | + LL| | | + LL| 2| if truthy { true } else { false }; + ^0 + LL| | + LL| 1| a(); + LL| 1| if truthy { a(); } + ^0 + LL| | + LL| 1| let b + LL| | = + LL| | | + LL| | | + LL| 2| if truthy { true } else { false }; + ^0 + LL| | + LL| 1| b(); + LL| 1| if truthy { b(); } + ^0 + LL| | + LL| 1| let c + LL| | = + LL| | | + LL| | | + LL| 2| if truthy { true } else { false }; + ^0 + LL| | + LL| 1| c(); + LL| 1| if truthy { c(); } + ^0 + LL| | + LL| 1| let d + LL| | = + LL| | | + LL| | | + LL| 2| if truthy { true } else { false }; + ^0 + LL| | + LL| 1| d(); + LL| 1| if truthy { d(); } + ^0 + LL| 1|} + diff --git a/tests/run-coverage/closure_bug.rs b/tests/run-coverage/closure_bug.rs new file mode 100644 index 00000000000..739bc5f0b51 --- /dev/null +++ b/tests/run-coverage/closure_bug.rs @@ -0,0 +1,44 @@ +// Regression test for #115930. +// All of these closures are identical, and should produce identical output in +// the coverage report. However, an unstable sort was causing them to be treated +// inconsistently when preparing coverage spans. + +fn main() { + let truthy = std::env::args().len() == 1; + + let a + = + | + | + if truthy { true } else { false }; + + a(); + if truthy { a(); } + + let b + = + | + | + if truthy { true } else { false }; + + b(); + if truthy { b(); } + + let c + = + | + | + if truthy { true } else { false }; + + c(); + if truthy { c(); } + + let d + = + | + | + if truthy { true } else { false }; + + d(); + if truthy { d(); } +} From 01b67f4b26f420b8713f11b04594f51e687f95fc Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 17 Sep 2023 14:21:19 +1000 Subject: [PATCH 230/250] coverage: Simplify sorting of coverage spans extracted from MIR Switching to `Ordering::then_with` makes control-flow less complicated, and there is no need to use `partial_cmp` here. --- .../src/graph/dominators/mod.rs | 6 +-- .../rustc_mir_transform/src/coverage/graph.rs | 8 +--- .../rustc_mir_transform/src/coverage/spans.rs | 41 +++++++------------ 3 files changed, 19 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 85ef2de9b5e..4075481e561 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -51,7 +51,7 @@ pub fn dominators(graph: &G) -> Dominators { // Traverse the graph, collecting a number of things: // // * Preorder mapping (to it, and back to the actual ordering) - // * Postorder mapping (used exclusively for rank_partial_cmp on the final product) + // * Postorder mapping (used exclusively for `cmp_in_dominator_order` on the final product) // * Parents for each vertex in the preorder tree // // These are all done here rather than through one of the 'standard' @@ -342,8 +342,8 @@ impl Dominators { /// relationship, the dominator will always precede the dominated. (The relative ordering /// of two unrelated nodes will also be consistent, but otherwise the order has no /// meaning.) This method cannot be used to determine if either Node dominates the other. - pub fn rank_partial_cmp(&self, lhs: Node, rhs: Node) -> Option { - self.post_order_rank[rhs].partial_cmp(&self.post_order_rank[lhs]) + pub fn cmp_in_dominator_order(&self, lhs: Node, rhs: Node) -> Ordering { + self.post_order_rank[rhs].cmp(&self.post_order_rank[lhs]) } /// Returns true if `a` dominates `b`. diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 60461691e7f..b6b0463614d 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -199,12 +199,8 @@ impl CoverageGraph { } #[inline(always)] - pub fn rank_partial_cmp( - &self, - a: BasicCoverageBlock, - b: BasicCoverageBlock, - ) -> Option { - self.dominators.as_ref().unwrap().rank_partial_cmp(a, b) + pub fn cmp_in_dominator_order(&self, a: BasicCoverageBlock, b: BasicCoverageBlock) -> Ordering { + self.dominators.as_ref().unwrap().cmp_in_dominator_order(a, b) } } diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index d254c1662e4..32e8ca25d31 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -12,7 +12,6 @@ use rustc_span::source_map::original_sp; use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol}; use std::cell::OnceCell; -use std::cmp::Ordering; #[derive(Debug, Copy, Clone)] pub(super) enum CoverageStatement { @@ -334,32 +333,20 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { initial_spans.push(CoverageSpan::for_fn_sig(self.fn_sig_span)); initial_spans.sort_by(|a, b| { - if a.span.lo() == b.span.lo() { - if a.span.hi() == b.span.hi() { - if a.is_in_same_bcb(b) { - Some(Ordering::Equal) - } else { - // Sort equal spans by dominator relationship (so dominators always come - // before the dominated equal spans). When later comparing two spans in - // order, the first will either dominate the second, or they will have no - // dominator relationship. - self.basic_coverage_blocks.rank_partial_cmp(a.bcb, b.bcb) - } - } else { - // Sort hi() in reverse order so shorter spans are attempted after longer spans. - // This guarantees that, if a `prev` span overlaps, and is not equal to, a - // `curr` span, the prev span either extends further left of the curr span, or - // they start at the same position and the prev span extends further right of - // the end of the curr span. - b.span.hi().partial_cmp(&a.span.hi()) - } - } else { - a.span.lo().partial_cmp(&b.span.lo()) - } - .unwrap() - // If two spans are otherwise identical, put closure spans first, - // as this seems to be what the refinement step expects. - .then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse()) + // First sort by span start. + Ord::cmp(&a.span.lo(), &b.span.lo()) + // If span starts are the same, sort by span end in reverse order. + // This ensures that if spans A and B are adjacent in the list, + // and they overlap but are not equal, then either: + // - Span A extends further left, or + // - Both have the same start and span A extends further right + .then_with(|| Ord::cmp(&a.span.hi(), &b.span.hi()).reverse()) + // If both spans are equal, sort the BCBs in dominator order, + // so that dominating BCBs come before other BCBs they dominate. + .then_with(|| self.basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb)) + // If two spans are otherwise identical, put closure spans first, + // as this seems to be what the refinement step expects. + .then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse()) }); initial_spans From 4eb1b527944ab69d769530b5bd96e848c50560e7 Mon Sep 17 00:00:00 2001 From: danakj Date: Thu, 7 Sep 2023 10:18:05 -0400 Subject: [PATCH 231/250] Enable ASAN/LSAN/TSAN for *-apple-ios-macabi The -macabi targets are iOS running on MacOS, and they use the runtime libraries for MacOS, thus they have the same sanitizers available as the *-apple-darwin targets. --- compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs | 3 ++- compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs | 3 ++- src/bootstrap/compile.rs | 2 ++ src/bootstrap/llvm.rs | 2 ++ src/tools/compiletest/src/util.rs | 5 +++++ 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs index e2df7e0bdcc..b29ab14e7f2 100644 --- a/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/aarch64_apple_ios_macabi.rs @@ -1,5 +1,5 @@ use super::apple_base::{opts, Arch}; -use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, Target, TargetOptions}; +use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, SanitizerSet, Target, TargetOptions}; pub fn target() -> Target { let llvm_target = "arm64-apple-ios14.0-macabi"; @@ -7,6 +7,7 @@ pub fn target() -> Target { let arch = Arch::Arm64_macabi; let mut base = opts("ios", arch); base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-target", llvm_target]); + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD; Target { llvm_target: llvm_target.into(), diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs index 50f359c357b..fd1926f2945 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs @@ -1,5 +1,5 @@ use super::apple_base::{opts, Arch}; -use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions}; +use crate::spec::{Cc, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let llvm_target = "x86_64-apple-ios14.0-macabi"; @@ -7,6 +7,7 @@ pub fn target() -> Target { let arch = Arch::X86_64_macabi; let mut base = opts("ios", arch); base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-target", llvm_target]); + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD; Target { llvm_target: llvm_target.into(), diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 2686a8c1752..e02415153a8 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -574,7 +574,9 @@ fn copy_sanitizers( || target == "aarch64-apple-darwin" || target == "aarch64-apple-ios" || target == "aarch64-apple-ios-sim" + || target == "aarch64-apple-ios-catalyst" || target == "x86_64-apple-ios" + || target == "x86_64-apple-ios-catalyst" { // Update the library’s install name to reflect that it has been renamed. apple_darwin_update_library_name(&dst, &format!("@rpath/{}", &runtime.name)); diff --git a/src/bootstrap/llvm.rs b/src/bootstrap/llvm.rs index 07288a1863a..7e5ade50ca7 100644 --- a/src/bootstrap/llvm.rs +++ b/src/bootstrap/llvm.rs @@ -1063,6 +1063,7 @@ fn supported_sanitizers( "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "aarch64-apple-ios" => darwin_libs("ios", &["asan", "tsan"]), "aarch64-apple-ios-sim" => darwin_libs("iossim", &["asan", "tsan"]), + "aarch64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "aarch64-unknown-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]), "aarch64-unknown-linux-gnu" => { common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"]) @@ -1073,6 +1074,7 @@ fn supported_sanitizers( "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "x86_64-unknown-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]), "x86_64-apple-ios" => darwin_libs("iossim", &["asan", "tsan"]), + "x86_64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "x86_64-unknown-freebsd" => common_libs("freebsd", "x86_64", &["asan", "msan", "tsan"]), "x86_64-unknown-netbsd" => { common_libs("netbsd", "x86_64", &["asan", "lsan", "msan", "tsan"]) diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 17bed38b65e..02648fe5c29 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -13,6 +13,7 @@ pub const ASAN_SUPPORTED_TARGETS: &[&str] = &[ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", + "aarch64-apple-ios-macabi", "aarch64-unknown-fuchsia", "aarch64-linux-android", "aarch64-unknown-linux-gnu", @@ -22,6 +23,7 @@ pub const ASAN_SUPPORTED_TARGETS: &[&str] = &[ "i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", + "x86_64-apple-ios-macabi", "x86_64-unknown-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", @@ -60,6 +62,7 @@ pub const LSAN_SUPPORTED_TARGETS: &[&str] = &[ // "aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", + "x86_64-apple-ios-macabi", "x86_64-unknown-linux-gnu", "s390x-unknown-linux-gnu", ]; @@ -75,9 +78,11 @@ pub const TSAN_SUPPORTED_TARGETS: &[&str] = &[ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", + "aarch64-apple-ios-macabi", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", + "x86_64-apple-ios-macabi", "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu", "s390x-unknown-linux-gnu", From b7e98e13cdbd6319fab38b6edc233e3d2925d567 Mon Sep 17 00:00:00 2001 From: danakj Date: Mon, 11 Sep 2023 10:33:50 -0400 Subject: [PATCH 232/250] Document that the macabi sanitizers are shared with darwin Do not rename and resign the darwin sanitizers a second time for macabi. --- src/bootstrap/compile.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index e02415153a8..4f19ffa83db 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -570,13 +570,14 @@ fn copy_sanitizers( let dst = libdir.join(&runtime.name); builder.copy(&runtime.path, &dst); + // The `aarch64-apple-ios-macabi` and `x86_64-apple-ios-macabi` are also supported for + // sanitizers, but they share a sanitizer runtime with `${arch}-apple-darwin`, so we do + // not list them here to rename and sign the runtime library. if target == "x86_64-apple-darwin" || target == "aarch64-apple-darwin" || target == "aarch64-apple-ios" || target == "aarch64-apple-ios-sim" - || target == "aarch64-apple-ios-catalyst" || target == "x86_64-apple-ios" - || target == "x86_64-apple-ios-catalyst" { // Update the library’s install name to reflect that it has been renamed. apple_darwin_update_library_name(&dst, &format!("@rpath/{}", &runtime.name)); From 354397f04dff7769c70dbaff9505056523c0bd22 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 18 Sep 2023 15:22:40 +0200 Subject: [PATCH 233/250] Move mobile topbar title creation entirely into JS --- src/librustdoc/html/static/js/main.js | 8 +++++--- src/librustdoc/html/templates/page.html | 3 +-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 097aa0b940d..eb256455b08 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -49,10 +49,12 @@ window.currentCrate = getVar("current-crate"); function setMobileTopbar() { // FIXME: It would be nicer to generate this text content directly in HTML, // but with the current code it's hard to get the right information in the right place. - const mobileLocationTitle = document.querySelector(".mobile-topbar h2"); + const mobileTopbar = document.querySelector(".mobile-topbar"); const locationTitle = document.querySelector(".sidebar h2.location"); - if (mobileLocationTitle && locationTitle) { - mobileLocationTitle.innerHTML = locationTitle.innerHTML; + if (mobileTopbar && locationTitle) { + const mobileTitle = document.createElement("h2"); + mobileTitle.innerHTML = locationTitle.innerHTML; + mobileTopbar.appendChild(mobileTitle); } } diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index b4b9c319165..579c782be09 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -84,8 +84,7 @@ {# #} {% endif %} {# #} -

{# #} - {# #} + {% endif %}