diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 557dfe09853b..bdfe7bfb8f1f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -283,3 +283,12 @@ impl NoArgsAttributeParser for RustcPreserveUbChecksParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPreserveUbChecks; } + +pub(crate) struct RustcNoImplicitBoundsParser; + +impl NoArgsAttributeParser for RustcNoImplicitBoundsParser { + const PATH: &[Symbol] = &[sym::rustc_no_implicit_bounds]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitBounds; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index ddb5ae33bc98..80dda5efd1c5 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -282,6 +282,7 @@ mod late { Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 4fb739403ced..853ecd3c91f7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1695,7 +1695,7 @@ fn suggest_ampmut<'tcx>( && let Either::Left(rhs_stmt_new) = body.stmt_at(*assign) && let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind && let rhs_span_new = rhs_stmt_new.source_info.span - && let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span) + && let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span_new) { (rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new); } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 361e98454e83..38b73a07a689 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -121,6 +121,11 @@ fn mir_borrowck( let (input_body, _) = tcx.mir_promoted(def); debug!("run query mir_borrowck: {}", tcx.def_path_str(def)); + // We should eagerly check stalled coroutine obligations from HIR typeck. + // Not doing so leads to silent normalization failures later, which will + // fail to register opaque types in the next solver. + tcx.check_coroutine_obligations(def)?; + let input_body: &Body<'_> = &input_body.borrow(); if let Some(guar) = input_body.tainted_by_errors { debug!("Skipping borrowck because of tainted body"); diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index d9c4179cca83..bfbf1272e5fc 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1177,6 +1177,9 @@ pub enum AttributeKind { /// Represents `#[rustc_no_implicit_autorefs]` RustcNoImplicitAutorefs, + /// Represents `#[rustc_no_implicit_bounds]` + RustcNoImplicitBounds, + /// Represents `#[rustc_non_const_trait_method]`. RustcNonConstTraitMethod, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index d55cb1d6f66a..ae9f9899ffd9 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -137,6 +137,7 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { RustcMustImplementOneOf { .. } => No, RustcNeverReturnsNullPointer => Yes, RustcNoImplicitAutorefs => Yes, + RustcNoImplicitBounds => No, RustcNonConstTraitMethod => No, // should be reported via other queries like `constness` RustcNounwind => No, RustcObjcClass { .. } => No, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index a1c3af5f999d..85e96e200c2e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -4,15 +4,16 @@ use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; -use rustc_hir::PolyTraitRef; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; +use rustc_hir::{PolyTraitRef, find_attr}; use rustc_middle::bug; use rustc_middle::ty::{ self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, Upcast, }; -use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; +use rustc_span::{ErrorGuaranteed, Ident, Span, kw}; use rustc_trait_selection::traits; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -170,7 +171,7 @@ pub(crate) fn add_implicit_sizedness_bounds( let tcx = self.tcx(); // Skip adding any default bounds if `#![rustc_no_implicit_bounds]` - if tcx.has_attr(CRATE_DEF_ID, sym::rustc_no_implicit_bounds) { + if find_attr!(tcx.get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcNoImplicitBounds) { return; } @@ -284,7 +285,8 @@ fn should_add_default_traits<'a>( context: ImpliedBoundsContext<'tcx>, ) -> bool { let collected = collect_bounds(hir_bounds, context, trait_def_id); - !self.tcx().has_attr(CRATE_DEF_ID, sym::rustc_no_implicit_bounds) && !collected.any() + !find_attr!(self.tcx().get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcNoImplicitBounds) + && !collected.any() } fn reject_duplicate_relaxed_bounds(&self, relaxed_bounds: SmallVec<[&PolyTraitRef<'_>; 1]>) { diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 42ef66847858..e474f106433d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1116,18 +1116,14 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { { tcx.ensure_ok().mir_drops_elaborated_and_const_checked(def_id); } - if tcx.is_coroutine(def_id.to_def_id()) { - tcx.ensure_ok().mir_coroutine_witnesses(def_id); - let _ = tcx.ensure_ok().check_coroutine_obligations( - tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(), + if tcx.is_coroutine(def_id.to_def_id()) + && (!tcx.is_async_drop_in_place_coroutine(def_id.to_def_id())) + { + // Eagerly check the unsubstituted layout for cycles. + tcx.ensure_ok().layout_of( + ty::TypingEnv::post_analysis(tcx, def_id.to_def_id()) + .as_query_input(tcx.type_of(def_id).instantiate_identity()), ); - if !tcx.is_async_drop_in_place_coroutine(def_id.to_def_id()) { - // Eagerly check the unsubstituted layout for cycles. - tcx.ensure_ok().layout_of( - ty::TypingEnv::post_analysis(tcx, def_id.to_def_id()) - .as_query_input(tcx.type_of(def_id).instantiate_identity()), - ); - } } }); }); diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index fa9995898ac2..a06f92792820 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -16,8 +16,7 @@ pub struct ImplicitCtxt<'a, 'tcx> { /// The current `TyCtxt`. pub tcx: TyCtxt<'tcx>, - /// The current query job, if any. This is updated by `JobOwner::start` in - /// `ty::query::plumbing` when executing a query. + /// The current query job, if any. pub query: Option, /// Used to prevent queries from calling too deeply. diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3f89ff577d59..f3c98f5d910e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -330,6 +330,7 @@ fn check_attributes( | AttributeKind::RustcMir(_) | AttributeKind::RustcNeverReturnsNullPointer | AttributeKind::RustcNoImplicitAutorefs + | AttributeKind::RustcNoImplicitBounds | AttributeKind::RustcNonConstTraitMethod | AttributeKind::RustcNounwind | AttributeKind::RustcObjcClass { .. } @@ -413,7 +414,6 @@ fn check_attributes( // crate-level attrs, are checked below | sym::feature | sym::register_tool - | sym::rustc_no_implicit_bounds | sym::test_runner, .. ] => {} diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index ba442d8a0081..88604c91d025 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -83,14 +83,18 @@ pub(crate) fn gather_active_jobs_inner<'tcx, K: Copy>( Some(()) } -/// A type representing the responsibility to execute the job in the `job` field. -/// This will poison the relevant query if dropped. -struct JobOwner<'tcx, K> +/// Guard object representing the responsibility to execute a query job and +/// mark it as completed. +/// +/// This will poison the relevant query key if it is dropped without calling +/// [`Self::complete`]. +struct ActiveJobGuard<'tcx, K> where K: Eq + Hash + Copy, { state: &'tcx QueryState<'tcx, K>, key: K, + key_hash: u64, } #[cold] @@ -137,20 +141,19 @@ fn handle_cycle_error<'tcx, C: QueryCache, const FLAGS: QueryFlags>( } } -impl<'tcx, K> JobOwner<'tcx, K> +impl<'tcx, K> ActiveJobGuard<'tcx, K> where K: Eq + Hash + Copy, { /// Completes the query by updating the query cache with the `result`, - /// signals the waiter and forgets the JobOwner, so it won't poison the query - fn complete(self, cache: &C, key_hash: u64, result: C::Value, dep_node_index: DepNodeIndex) + /// signals the waiter, and forgets the guard so it won't poison the query. + fn complete(self, cache: &C, result: C::Value, dep_node_index: DepNodeIndex) where C: QueryCache, { - let key = self.key; - let state = self.state; - - // Forget ourself so our destructor won't poison the query + // Forget ourself so our destructor won't poison the query. + // (Extract fields by value first to make sure we don't leak anything.) + let Self { state, key, key_hash }: Self = self; mem::forget(self); // Mark as complete before we remove the job from the active state @@ -174,7 +177,7 @@ fn complete(self, cache: &C, key_hash: u64, result: C::Value, dep_node_index: } } -impl<'tcx, K> Drop for JobOwner<'tcx, K> +impl<'tcx, K> Drop for ActiveJobGuard<'tcx, K> where K: Eq + Hash + Copy, { @@ -182,11 +185,10 @@ impl<'tcx, K> Drop for JobOwner<'tcx, K> #[cold] fn drop(&mut self) { // Poison the query so jobs waiting on it panic. - let state = self.state; + let Self { state, key, key_hash } = *self; let job = { - let key_hash = sharded::make_hash(&self.key); let mut shard = state.active.lock_shard_by_hash(key_hash); - match shard.find_entry(key_hash, equivalent_key(&self.key)) { + match shard.find_entry(key_hash, equivalent_key(&key)) { Err(_) => panic!(), Ok(occupied) => { let ((key, value), vacant) = occupied.remove(); @@ -342,11 +344,13 @@ fn execute_job<'tcx, C: QueryCache, const FLAGS: QueryFlags, const INCR: bool>( id: QueryJobId, dep_node: Option, ) -> (C::Value, Option) { - // Use `JobOwner` so the query will be poisoned if executing it panics. - let job_owner = JobOwner { state, key }; + // Set up a guard object that will automatically poison the query if a + // panic occurs while executing the query (or any intermediate plumbing). + let job_guard = ActiveJobGuard { state, key, key_hash }; debug_assert_eq!(qcx.tcx.dep_graph.is_fully_enabled(), INCR); + // Delegate to another function to actually execute the query job. let (result, dep_node_index) = if INCR { execute_job_incr(query, qcx, qcx.tcx.dep_graph.data().unwrap(), key, dep_node, id) } else { @@ -388,7 +392,9 @@ fn execute_job<'tcx, C: QueryCache, const FLAGS: QueryFlags, const INCR: bool>( } } } - job_owner.complete(cache, key_hash, result, dep_node_index); + + // Tell the guard to perform completion bookkeeping, and also to not poison the query. + job_guard.complete(cache, result, dep_node_index); (result, Some(dep_node_index)) } diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs index fb735b54dd82..4b6f5b655760 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -19,8 +19,15 @@ pub(crate) fn target() -> Target { pre_link_args, post_link_args, relocation_model: RelocModel::Pic, + // crt_static should always be true for an executable and always false + // for a shared library. There is no easy way to indicate this and it + // doesn't seem to matter much so we set crt_static_allows_dylibs to + // true and leave crt_static as true when linking dynamic libraries. + // wasi also sets crt_static_allows_dylibs: true so this is at least + // aligned between wasm targets. crt_static_respected: true, crt_static_default: true, + crt_static_allows_dylibs: true, panic_strategy: PanicStrategy::Unwind, no_default_libraries: false, families: cvs!["unix", "wasm"], diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 584c8e2a27c8..bd48068eb579 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -12,7 +12,7 @@ use smallvec::{SmallVec, smallvec}; use tracing::debug; -use crate::traits::{impossible_predicates, is_vtable_safe_method}; +use crate::traits::is_vtable_safe_method; #[derive(Clone, Debug)] pub enum VtblSegment<'tcx> { @@ -271,11 +271,7 @@ fn vtable_entries<'tcx>( // do not hold for this particular set of type parameters. // Note that this method could then never be called, so we // do not want to try and codegen it, in that case (see #23435). - let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args); - if impossible_predicates( - tcx, - predicates.map(|(predicate, _)| predicate).collect(), - ) { + if tcx.instantiate_and_check_impossible_predicates((def_id, args)) { debug!("vtable_entries: predicates do not hold"); return VtblEntry::Vacant; } diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 661ea4ab6a27..a9e7c49515c7 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -689,6 +689,30 @@ impl, U> CoerceUnsized> for Cell {} #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U> DispatchFromDyn> for Cell {} +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsRef<[Cell; N]> for Cell<[T; N]> { + #[inline] + fn as_ref(&self) -> &[Cell; N] { + self.as_array_of_cells() + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsRef<[Cell]> for Cell<[T; N]> { + #[inline] + fn as_ref(&self) -> &[Cell] { + &*self.as_array_of_cells() + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsRef<[Cell]> for Cell<[T]> { + #[inline] + fn as_ref(&self) -> &[Cell] { + self.as_slice_of_cells() + } +} + impl Cell<[T]> { /// Returns a `&[Cell]` from a `&Cell<[T]>` /// diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 320eb97f83a4..594147720193 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1532,6 +1532,56 @@ pub const fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { } } +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl From<[MaybeUninit; N]> for MaybeUninit<[T; N]> { + #[inline] + fn from(arr: [MaybeUninit; N]) -> Self { + arr.transpose() + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsRef<[MaybeUninit; N]> for MaybeUninit<[T; N]> { + #[inline] + fn as_ref(&self) -> &[MaybeUninit; N] { + // SAFETY: T and MaybeUninit have the same layout + unsafe { &*ptr::from_ref(self).cast() } + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsRef<[MaybeUninit]> for MaybeUninit<[T; N]> { + #[inline] + fn as_ref(&self) -> &[MaybeUninit] { + &*AsRef::<[MaybeUninit; N]>::as_ref(self) + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsMut<[MaybeUninit; N]> for MaybeUninit<[T; N]> { + #[inline] + fn as_mut(&mut self) -> &mut [MaybeUninit; N] { + // SAFETY: T and MaybeUninit have the same layout + unsafe { &mut *ptr::from_mut(self).cast() } + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl AsMut<[MaybeUninit]> for MaybeUninit<[T; N]> { + #[inline] + fn as_mut(&mut self) -> &mut [MaybeUninit] { + &mut *AsMut::<[MaybeUninit; N]>::as_mut(self) + } +} + +#[stable(feature = "more_conversion_trait_impls", since = "CURRENT_RUSTC_VERSION")] +impl From> for [MaybeUninit; N] { + #[inline] + fn from(arr: MaybeUninit<[T; N]>) -> Self { + arr.transpose() + } +} + impl [MaybeUninit; N] { /// Transposes a `[MaybeUninit; N]` into a `MaybeUninit<[T; N]>`. /// diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index a289b0d6df40..ac096afb38af 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -2175,7 +2175,7 @@ unsafe impl Sync for ChunksExactMut<'_, T> where T: Sync {} /// /// [`array_windows`]: slice::array_windows /// [slices]: slice -#[derive(Debug, Clone, Copy)] +#[derive(Debug)] #[stable(feature = "array_windows", since = "1.94.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ArrayWindows<'a, T: 'a, const N: usize> { @@ -2189,6 +2189,14 @@ pub(super) const fn new(slice: &'a [T]) -> Self { } } +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "array_windows", since = "1.94.0")] +impl Clone for ArrayWindows<'_, T, N> { + fn clone(&self) -> Self { + Self { v: self.v } + } +} + #[stable(feature = "array_windows", since = "1.94.0")] impl<'a, T, const N: usize> Iterator for ArrayWindows<'a, T, N> { type Item = &'a [T; N]; @@ -2224,6 +2232,14 @@ fn nth(&mut self, n: usize) -> Option { fn last(self) -> Option { self.v.last_chunk() } + + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { + // SAFETY: since the caller guarantees that `idx` is in bounds, + // which means that `idx` cannot overflow an `isize`, and the + // "slice" created by `cast_array` is a subslice of `self.v` + // thus is guaranteed to be valid for the lifetime `'a` of `self.v`. + unsafe { &*self.v.as_ptr().add(idx).cast_array() } + } } #[stable(feature = "array_windows", since = "1.94.0")] @@ -2252,6 +2268,22 @@ fn is_empty(&self) -> bool { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ArrayWindows<'_, T, N> {} + +#[stable(feature = "array_windows", since = "1.94.0")] +impl FusedIterator for ArrayWindows<'_, T, N> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for ArrayWindows<'_, T, N> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNoCoerce for ArrayWindows<'_, T, N> { + const MAY_HAVE_SIDE_EFFECT: bool = false; +} + /// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a /// time), starting at the end of the slice. /// diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 8f4a79b389f6..02a408802b6f 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -121,7 +121,7 @@ impl Methods { } } } -with_api!(self, define_client_side); +with_api!(define_client_side, TokenStream, Span, Symbol); struct Bridge<'a> { /// Reusable buffer (only `clear`-ed, never shrunk), primarily @@ -129,7 +129,7 @@ struct Bridge<'a> { cached_buffer: Buffer, /// Server-side function that the client uses to make requests. - dispatch: closure::Closure<'a, Buffer, Buffer>, + dispatch: closure::Closure<'a>, /// Provided globals for this macro expansion. globals: ExpnGlobals, diff --git a/library/proc_macro/src/bridge/closure.rs b/library/proc_macro/src/bridge/closure.rs index e5133907854b..88c4dd6630b1 100644 --- a/library/proc_macro/src/bridge/closure.rs +++ b/library/proc_macro/src/bridge/closure.rs @@ -1,10 +1,12 @@ -//! Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`. +//! Closure type (equivalent to `&mut dyn FnMut(Buffer) -> Buffer`) that's `repr(C)`. use std::marker::PhantomData; +use super::Buffer; + #[repr(C)] -pub(super) struct Closure<'a, A, R> { - call: unsafe extern "C" fn(*mut Env, A) -> R, +pub(super) struct Closure<'a> { + call: extern "C" fn(*mut Env, Buffer) -> Buffer, env: *mut Env, // Prevent Send and Sync impls. // @@ -14,17 +16,17 @@ pub(super) struct Closure<'a, A, R> { struct Env; -impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> { +impl<'a, F: FnMut(Buffer) -> Buffer> From<&'a mut F> for Closure<'a> { fn from(f: &'a mut F) -> Self { - unsafe extern "C" fn call R>(env: *mut Env, arg: A) -> R { + extern "C" fn call Buffer>(env: *mut Env, arg: Buffer) -> Buffer { unsafe { (*(env as *mut _ as *mut F))(arg) } } - Closure { call: call::, env: f as *mut _ as *mut Env, _marker: PhantomData } + Closure { call: call::, env: f as *mut _ as *mut Env, _marker: PhantomData } } } -impl<'a, A, R> Closure<'a, A, R> { - pub(super) fn call(&mut self, arg: A) -> R { - unsafe { (self.call)(self.env, arg) } +impl<'a> Closure<'a> { + pub(super) fn call(&mut self, arg: Buffer) -> Buffer { + (self.call)(self.env, arg) } } diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 244ab7d81b02..6a9027046af0 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -18,71 +18,67 @@ /// Higher-order macro describing the server RPC API, allowing automatic /// generation of type-safe Rust APIs, both client-side and server-side. /// -/// `with_api!(MySelf, my_macro)` expands to: +/// `with_api!(my_macro, MyTokenStream, MySpan, MySymbol)` expands to: /// ```rust,ignore (pseudo-code) /// my_macro! { -/// fn lit_character(ch: char) -> MySelf::Literal; -/// fn lit_span(lit: &MySelf::Literal) -> MySelf::Span; -/// fn lit_set_span(lit: &mut MySelf::Literal, span: MySelf::Span); +/// fn ts_clone(stream: &MyTokenStream) -> MyTokenStream; +/// fn span_debug(span: &MySpan) -> String; /// // ... /// } /// ``` /// -/// The first argument serves to customize the argument/return types, -/// to enable several different usecases: -/// -/// If `MySelf` is just `Self`, then the types are only valid inside -/// a trait or a trait impl, where the trait has associated types -/// for each of the API types. If non-associated types are desired, -/// a module name (`self` in practice) can be used instead of `Self`. +/// The second (`TokenStream`), third (`Span`) and fourth (`Symbol`) +/// argument serve to customize the argument/return types that need +/// special handling, to enable several different representations of +/// these types. macro_rules! with_api { - ($S:ident, $m:ident) => { + ($m:ident, $TokenStream: path, $Span: path, $Symbol: path) => { $m! { fn injected_env_var(var: &str) -> Option; fn track_env_var(var: &str, value: Option<&str>); fn track_path(path: &str); - fn literal_from_str(s: &str) -> Result, ()>; - fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>); + fn literal_from_str(s: &str) -> Result, ()>; + fn emit_diagnostic(diagnostic: Diagnostic<$Span>); - fn ts_drop(stream: $S::TokenStream); - fn ts_clone(stream: &$S::TokenStream) -> $S::TokenStream; - fn ts_is_empty(stream: &$S::TokenStream) -> bool; - fn ts_expand_expr(stream: &$S::TokenStream) -> Result<$S::TokenStream, ()>; - fn ts_from_str(src: &str) -> $S::TokenStream; - fn ts_to_string(stream: &$S::TokenStream) -> String; + fn ts_drop(stream: $TokenStream); + fn ts_clone(stream: &$TokenStream) -> $TokenStream; + fn ts_is_empty(stream: &$TokenStream) -> bool; + fn ts_expand_expr(stream: &$TokenStream) -> Result<$TokenStream, ()>; + fn ts_from_str(src: &str) -> $TokenStream; + fn ts_to_string(stream: &$TokenStream) -> String; fn ts_from_token_tree( - tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>, - ) -> $S::TokenStream; + tree: TokenTree<$TokenStream, $Span, $Symbol>, + ) -> $TokenStream; fn ts_concat_trees( - base: Option<$S::TokenStream>, - trees: Vec>, - ) -> $S::TokenStream; + base: Option<$TokenStream>, + trees: Vec>, + ) -> $TokenStream; fn ts_concat_streams( - base: Option<$S::TokenStream>, - streams: Vec<$S::TokenStream>, - ) -> $S::TokenStream; + base: Option<$TokenStream>, + streams: Vec<$TokenStream>, + ) -> $TokenStream; fn ts_into_trees( - stream: $S::TokenStream - ) -> Vec>; + stream: $TokenStream + ) -> Vec>; - fn span_debug(span: $S::Span) -> String; - fn span_parent(span: $S::Span) -> Option<$S::Span>; - fn span_source(span: $S::Span) -> $S::Span; - fn span_byte_range(span: $S::Span) -> Range; - fn span_start(span: $S::Span) -> $S::Span; - fn span_end(span: $S::Span) -> $S::Span; - fn span_line(span: $S::Span) -> usize; - fn span_column(span: $S::Span) -> usize; - fn span_file(span: $S::Span) -> String; - fn span_local_file(span: $S::Span) -> Option; - fn span_join(span: $S::Span, other: $S::Span) -> Option<$S::Span>; - fn span_subspan(span: $S::Span, start: Bound, end: Bound) -> Option<$S::Span>; - fn span_resolved_at(span: $S::Span, at: $S::Span) -> $S::Span; - fn span_source_text(span: $S::Span) -> Option; - fn span_save_span(span: $S::Span) -> usize; - fn span_recover_proc_macro_span(id: usize) -> $S::Span; + fn span_debug(span: $Span) -> String; + fn span_parent(span: $Span) -> Option<$Span>; + fn span_source(span: $Span) -> $Span; + fn span_byte_range(span: $Span) -> Range; + fn span_start(span: $Span) -> $Span; + fn span_end(span: $Span) -> $Span; + fn span_line(span: $Span) -> usize; + fn span_column(span: $Span) -> usize; + fn span_file(span: $Span) -> String; + fn span_local_file(span: $Span) -> Option; + fn span_join(span: $Span, other: $Span) -> Option<$Span>; + fn span_subspan(span: $Span, start: Bound, end: Bound) -> Option<$Span>; + fn span_resolved_at(span: $Span, at: $Span) -> $Span; + fn span_source_text(span: $Span) -> Option; + fn span_save_span(span: $Span) -> usize; + fn span_recover_proc_macro_span(id: usize) -> $Span; - fn symbol_normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>; + fn symbol_normalize_and_validate_ident(string: &str) -> Result<$Symbol, ()>; } }; } @@ -126,7 +122,7 @@ pub struct BridgeConfig<'a> { input: Buffer, /// Server-side function that the client uses to make requests. - dispatch: closure::Closure<'a, Buffer, Buffer>, + dispatch: closure::Closure<'a>, /// If 'true', always invoke the default panic hook force_show_panics: bool, @@ -146,7 +142,7 @@ pub(super) enum ApiTags { rpc_encode_decode!(enum ApiTags { $($method),* }); } } -with_api!(self, declare_tags); +with_api!(declare_tags, __, __, __); /// Helper to wrap associated types to allow trait impl dispatch. /// That is, normally a pair of impls for `T::Foo` and `T::Bar` @@ -173,7 +169,7 @@ fn unmark(self) -> Self::Unmarked { self.value } } -impl<'a, T, M> Mark for &'a Marked { +impl<'a, T> Mark for &'a Marked { type Unmarked = &'a T; fn mark(_: Self::Unmarked) -> Self { unreachable!() @@ -220,6 +216,8 @@ fn unmark(self) -> Self::Unmarked { Delimiter, LitKind, Level, + Bound, + Range, } rpc_encode_decode!( @@ -318,7 +316,7 @@ macro_rules! compound_traits { }; } -compound_traits!( +rpc_encode_decode!( enum Bound { Included(x), Excluded(x), @@ -390,7 +388,7 @@ pub struct Literal { pub span: Span, } -compound_traits!(struct Literal { kind, symbol, suffix, span }); +compound_traits!(struct Literal { kind, symbol, suffix, span }); #[derive(Clone)] pub enum TokenTree { @@ -434,6 +432,6 @@ pub struct ExpnGlobals { struct ExpnGlobals { def_site, call_site, mixed_site } ); -compound_traits!( +rpc_encode_decode!( struct Range { start, end } ); diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 63329c8c0260..7fee8654bc78 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -52,45 +52,37 @@ fn decode(r: &mut &'a [u8], s: &mut S) -> Self { } }; (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => { - impl),+)?> Encode for $name $(<$($T),+>)? { - fn encode(self, w: &mut Buffer, s: &mut S) { - // HACK(eddyb): `Tag` enum duplicated between the - // two impls as there's no other place to stash it. - #[allow(non_camel_case_types)] - #[repr(u8)] - enum Tag { $($variant),* } + #[allow(non_upper_case_globals, non_camel_case_types)] + const _: () = { + #[repr(u8)] enum Tag { $($variant),* } - match self { - $($name::$variant $(($field))* => { - (Tag::$variant as u8).encode(w, s); - $($field.encode(w, s);)* - })* + $(const $variant: u8 = Tag::$variant as u8;)* + + impl),+)?> Encode for $name $(<$($T),+>)? { + fn encode(self, w: &mut Buffer, s: &mut S) { + match self { + $($name::$variant $(($field))* => { + $variant.encode(w, s); + $($field.encode(w, s);)* + })* + } } } - } - impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S> - for $name $(<$($T),+>)? - { - fn decode(r: &mut &'a [u8], s: &mut S) -> Self { - // HACK(eddyb): `Tag` enum duplicated between the - // two impls as there's no other place to stash it. - #[allow(non_upper_case_globals, non_camel_case_types)] - mod tag { - #[repr(u8)] enum Tag { $($variant),* } - - $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* - } - - match u8::decode(r, s) { - $(tag::$variant => { - $(let $field = Decode::decode(r, s);)* - $name::$variant $(($field))* - })* - _ => unreachable!(), + impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S> + for $name $(<$($T),+>)? + { + fn decode(r: &mut &'a [u8], s: &mut S) -> Self { + match u8::decode(r, s) { + $($variant => { + $(let $field = Decode::decode(r, s);)* + $name::$variant $(($field))* + })* + _ => unreachable!(), + } } } - } + }; } } diff --git a/library/proc_macro/src/bridge/selfless_reify.rs b/library/proc_macro/src/bridge/selfless_reify.rs index a53550e0b9e0..1a9951af8c9f 100644 --- a/library/proc_macro/src/bridge/selfless_reify.rs +++ b/library/proc_macro/src/bridge/selfless_reify.rs @@ -38,47 +38,27 @@ use std::mem; -// FIXME(eddyb) this could be `trait` impls except for the `const fn` requirement. -macro_rules! define_reify_functions { - ($( - fn $name:ident $(<$($param:ident),*>)? - for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty; - )+) => { - $(pub(super) const fn $name< - $($($param,)*)? - F: Fn($($arg_ty),*) -> $ret_ty + Copy - >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { - // FIXME(eddyb) describe the `F` type (e.g. via `type_name::`) once panic - // formatting becomes possible in `const fn`. - const { assert!(size_of::() == 0, "selfless_reify: closure must be zero-sized"); } - - $(extern $abi)? fn wrapper< - $($($param,)*)? - F: Fn($($arg_ty),*) -> $ret_ty + Copy - >($($arg: $arg_ty),*) -> $ret_ty { - let f = unsafe { - // SAFETY: `F` satisfies all criteria for "out of thin air" - // reconstructability (see module-level doc comment). - mem::MaybeUninit::::uninit().assume_init() - }; - f($($arg),*) - } - let _f_proof = f; - wrapper::< - $($($param,)*)? - F - > - })+ +pub(super) const fn reify_to_extern_c_fn_hrt_bridge< + R, + F: Fn(super::BridgeConfig<'_>) -> R + Copy, +>( + f: F, +) -> extern "C" fn(super::BridgeConfig<'_>) -> R { + // FIXME(eddyb) describe the `F` type (e.g. via `type_name::`) once panic + // formatting becomes possible in `const fn`. + const { + assert!(size_of::() == 0, "selfless_reify: closure must be zero-sized"); } -} - -define_reify_functions! { - fn _reify_to_extern_c_fn_unary for extern "C" fn(arg: A) -> R; - - // HACK(eddyb) this abstraction is used with `for<'a> fn(BridgeConfig<'a>) - // -> T` but that doesn't work with just `reify_to_extern_c_fn_unary` - // because of the `fn` pointer type being "higher-ranked" (i.e. the - // `for<'a>` binder). - // FIXME(eddyb) try to remove the lifetime from `BridgeConfig`, that'd help. - fn reify_to_extern_c_fn_hrt_bridge for extern "C" fn(bridge: super::BridgeConfig<'_>) -> R; + extern "C" fn wrapper) -> R + Copy>( + bridge: super::BridgeConfig<'_>, + ) -> R { + let f = unsafe { + // SAFETY: `F` satisfies all criteria for "out of thin air" + // reconstructability (see module-level doc comment). + mem::conjure_zst::() + }; + f(bridge) + } + let _f_proof = f; + wrapper:: } diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 073ddb554994..a3c6a232264e 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -58,12 +58,12 @@ struct Dispatcher { server: S, } -macro_rules! define_server_dispatcher_impl { +macro_rules! define_server { ( $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* ) => { pub trait Server { - type TokenStream: 'static + Clone; + type TokenStream: 'static + Clone + Default; type Span: 'static + Copy + Eq + Hash; type Symbol: 'static; @@ -77,22 +77,20 @@ pub trait Server { $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?;)* } + } +} +with_api!(define_server, Self::TokenStream, Self::Span, Self::Symbol); +macro_rules! define_dispatcher { + ( + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + ) => { // FIXME(eddyb) `pub` only for `ExecutionStrategy` below. pub trait DispatcherTrait { - // HACK(eddyb) these are here to allow `Self::$name` to work below. - type TokenStream; - type Span; - type Symbol; - fn dispatch(&mut self, buf: Buffer) -> Buffer; } impl DispatcherTrait for Dispatcher { - type TokenStream = MarkedTokenStream; - type Span = MarkedSpan; - type Symbol = MarkedSymbol; - fn dispatch(&mut self, mut buf: Buffer) -> Buffer { let Dispatcher { handle_store, server } = self; @@ -127,7 +125,7 @@ fn dispatch(&mut self, mut buf: Buffer) -> Buffer { } } } -with_api!(Self, define_server_dispatcher_impl); +with_api!(define_dispatcher, MarkedTokenStream, MarkedSpan, MarkedSymbol); pub trait ExecutionStrategy { fn run_bridge_and_client( @@ -312,7 +310,6 @@ pub fn run( ) -> Result where S: Server, - S::TokenStream: Default, { let client::Client { handle_counters, run, _marker } = *self; run_server( @@ -338,7 +335,6 @@ pub fn run( ) -> Result where S: Server, - S::TokenStream: Default, { let client::Client { handle_counters, run, _marker } = *self; run_server( diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 49b6f2ae41f8..e2f39c015bdd 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -27,6 +27,7 @@ #![feature(restricted_std)] #![feature(rustc_attrs)] #![feature(extend_one)] +#![feature(mem_conjure_zst)] #![recursion_limit = "256"] #![allow(internal_features)] #![deny(ffi_unwind_calls)] diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 25bd7005b994..14b41a427f1e 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -19,6 +19,20 @@ //! matter the platform or filesystem. An exception to this is made for Windows //! drive letters. //! +//! ## Path normalization +//! +//! Several methods in this module perform basic path normalization by disregarding +//! repeated separators, non-leading `.` components, and trailing separators. These include: +//! - Methods for iteration, such as [`Path::components`] and [`Path::iter`] +//! - Methods for inspection, such as [`Path::has_root`] +//! - Comparisons using [`PartialEq`], [`PartialOrd`], and [`Ord`] +//! +//! [`Path::join`] and [`PathBuf::push`] also disregard trailing slashes. +//! +// FIXME(normalize_lexically): mention normalize_lexically once stable +//! These methods **do not** resolve `..` components or symlinks. For full normalization +//! including `..` resolution, use [`Path::canonicalize`] (which does access the filesystem). +//! //! ## Simple usage //! //! Path manipulation includes both parsing components from slices and building diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index c71a350ffd30..54c0f2ec7430 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -949,7 +949,9 @@ fn get_current_target_config( // actually be changed with `-C` flags. for config in query_rustc_output( config, - &["--print=cfg", "--target", &config.target], + // `-Zunstable-options` is necessary when compiletest is running with custom targets + // (such as synthetic targets used to bless mir-opt tests). + &["-Zunstable-options", "--print=cfg", "--target", &config.target], Default::default(), ) .trim() diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 9813ac7ff500..230578d79ffb 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -249,6 +249,7 @@ "only-unix", "only-visionos", "only-wasm32", + "only-wasm32-unknown-emscripten", "only-wasm32-unknown-unknown", "only-wasm32-wasip1", "only-watchos", diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 1804490a5f12..742790a50fce 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1670,6 +1670,11 @@ fn make_compile_args( if self.props.force_host { &*self.config.host } else { &*self.config.target }; compiler.arg(&format!("--target={}", target)); + if target.ends_with(".json") { + // `-Zunstable-options` is necessary when compiletest is running with custom targets + // (such as synthetic targets used to bless mir-opt tests). + compiler.arg("-Zunstable-options"); + } } self.set_revision_flags(&mut compiler); diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 2cf3e37a43bc..755ae55eea46 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1614,9 +1614,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-traits" @@ -2453,9 +2453,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa" -version = "0.25.2" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e2aa2fca57727371eeafc975acc8e6f4c52f8166a78035543f6ee1c74c2dcc" +checksum = "f77debccd43ba198e9cee23efd7f10330ff445e46a98a2b107fed9094a1ee676" dependencies = [ "boxcar", "crossbeam-queue", @@ -2478,15 +2478,15 @@ dependencies = [ [[package]] name = "salsa-macro-rules" -version = "0.25.2" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfc2a1e7bf06964105515451d728f2422dedc3a112383324a00b191a5c397a3" +checksum = "ea07adbf42d91cc076b7daf3b38bc8168c19eb362c665964118a89bc55ef19a5" [[package]] name = "salsa-macros" -version = "0.25.2" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d844c1aa34946da46af683b5c27ec1088a3d9d84a2b837a108223fd830220e1" +checksum = "d16d4d8b66451b9c75ddf740b7fc8399bc7b8ba33e854a5d7526d18708f67b05" dependencies = [ "proc-macro2", "quote", @@ -2914,9 +2914,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.44" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", @@ -2924,22 +2924,22 @@ dependencies = [ "num-conv", "num_threads", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.24" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 2288933a96cc..04559f15eda4 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -135,13 +135,13 @@ rayon = "1.10.0" rowan = "=0.15.17" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it -salsa = { version = "0.25.2", default-features = false, features = [ +salsa = { version = "0.26", default-features = false, features = [ "rayon", "salsa_unstable", "macros", "inventory", ] } -salsa-macros = "0.25.2" +salsa-macros = "0.26" semver = "1.0.26" serde = { version = "1.0.219" } serde_derive = { version = "1.0.219" } @@ -192,8 +192,6 @@ unused_lifetimes = "warn" unreachable_pub = "warn" [workspace.lints.clippy] -# FIXME Remove the tidy test once the lint table is stable - ## lint groups complexity = { level = "warn", priority = -1 } correctness = { level = "deny", priority = -1 } diff --git a/src/tools/rust-analyzer/bench_data/glorious_old_parser b/src/tools/rust-analyzer/bench_data/glorious_old_parser index f593f2b2955a..8136daa8329f 100644 Binary files a/src/tools/rust-analyzer/bench_data/glorious_old_parser and b/src/tools/rust-analyzer/bench_data/glorious_old_parser differ diff --git a/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs b/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs index 13fb05d56547..dd419f48fc7e 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs @@ -60,7 +60,7 @@ fn hash(&self, state: &mut H) { } } - impl zalsa_struct_::HashEqLike for EditionedFileIdData { + impl zalsa_::HashEqLike for EditionedFileIdData { #[inline] fn hash(&self, state: &mut H) { Hash::hash(self, state); diff --git a/src/tools/rust-analyzer/crates/edition/src/lib.rs b/src/tools/rust-analyzer/crates/edition/src/lib.rs index f1a1fe596493..eb4cec39dce0 100644 --- a/src/tools/rust-analyzer/crates/edition/src/lib.rs +++ b/src/tools/rust-analyzer/crates/edition/src/lib.rs @@ -16,8 +16,6 @@ impl Edition { pub const DEFAULT: Edition = Edition::Edition2015; pub const LATEST: Edition = Edition::Edition2024; pub const CURRENT: Edition = Edition::Edition2024; - /// The current latest stable edition, note this is usually not the right choice in code. - pub const CURRENT_FIXME: Edition = Edition::Edition2024; pub fn from_u32(u32: u32) -> Edition { match u32 { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 4fbf6d951779..701586c25838 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -426,7 +426,7 @@ pub struct ExprCollector<'db> { /// and we need to find the current definition. So we track the number of definitions we saw. current_block_legacy_macro_defs_count: FxHashMap, - current_try_block_label: Option, + current_try_block: Option, label_ribs: Vec, unowned_bindings: Vec, @@ -472,6 +472,13 @@ enum Awaitable { No(&'static str), } +enum TryBlock { + // `try { ... }` + Homogeneous { label: LabelId }, + // `try bikeshed Ty { ... }` + Heterogeneous { label: LabelId }, +} + #[derive(Debug, Default)] struct BindingList { map: FxHashMap<(Name, HygieneId), BindingId>, @@ -532,7 +539,7 @@ pub fn new( lang_items: OnceCell::new(), store: ExpressionStoreBuilder::default(), expander, - current_try_block_label: None, + current_try_block: None, is_lowering_coroutine: false, label_ribs: Vec::new(), unowned_bindings: Vec::new(), @@ -1069,7 +1076,9 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option { self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr) } ast::Expr::BlockExpr(e) => match e.modifier() { - Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e), + Some(ast::BlockModifier::Try { try_token: _, bikeshed_token: _, result_type }) => { + self.desugar_try_block(e, result_type) + } Some(ast::BlockModifier::Unsafe(_)) => { self.collect_block_(e, |id, statements, tail| Expr::Unsafe { id, @@ -1344,7 +1353,7 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option { .map(|it| this.lower_type_ref_disallow_impl_trait(it)); let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine); - let prev_try_block_label = this.current_try_block_label.take(); + let prev_try_block = this.current_try_block.take(); let awaitable = if e.async_token().is_some() { Awaitable::Yes @@ -1369,7 +1378,7 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option { let capture_by = if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; this.is_lowering_coroutine = prev_is_lowering_coroutine; - this.current_try_block_label = prev_try_block_label; + this.current_try_block = prev_try_block; this.alloc_expr( Expr::Closure { args: args.into(), @@ -1686,11 +1695,15 @@ fn with_binding_owner(&mut self, create_expr: impl FnOnce(&mut Self) -> ExprId) /// Desugar `try { ; }` into `': { ; ::std::ops::Try::from_output() }`, /// `try { ; }` into `': { ; ::std::ops::Try::from_output(()) }` /// and save the `` to use it as a break target for desugaring of the `?` operator. - fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId { + fn desugar_try_block(&mut self, e: BlockExpr, result_type: Option) -> ExprId { let try_from_output = self.lang_path(self.lang_items().TryTraitFromOutput); let label = self.generate_new_name(); let label = self.alloc_label_desugared(Label { name: label }, AstPtr::new(&e).wrap_right()); - let old_label = self.current_try_block_label.replace(label); + let try_block_info = match result_type { + Some(_) => TryBlock::Heterogeneous { label }, + None => TryBlock::Homogeneous { label }, + }; + let old_try_block = self.current_try_block.replace(try_block_info); let ptr = AstPtr::new(&e).upcast(); let (btail, expr_id) = self.with_labeled_rib(label, HygieneId::ROOT, |this| { @@ -1720,8 +1733,38 @@ fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId { unreachable!("block was lowered to non-block"); }; *tail = Some(next_tail); - self.current_try_block_label = old_label; - expr_id + self.current_try_block = old_try_block; + match result_type { + Some(ty) => { + // `{ let : = ; }` + let name = self.generate_new_name(); + let type_ref = self.lower_type_ref_disallow_impl_trait(ty); + let binding = self.alloc_binding( + name.clone(), + BindingAnnotation::Unannotated, + HygieneId::ROOT, + ); + let pat = self.alloc_pat_desugared(Pat::Bind { id: binding, subpat: None }); + self.add_definition_to_binding(binding, pat); + let tail_expr = + self.alloc_expr_desugared_with_ptr(Expr::Path(Path::from(name)), ptr); + self.alloc_expr_desugared_with_ptr( + Expr::Block { + id: None, + statements: Box::new([Statement::Let { + pat, + type_ref: Some(type_ref), + initializer: Some(expr_id), + else_branch: None, + }]), + tail: Some(tail_expr), + label: None, + }, + ptr, + ) + } + None => expr_id, + } } /// Desugar `ast::WhileExpr` from: `[opt_ident]: while ` into: @@ -1863,6 +1906,8 @@ fn collect_for_loop(&mut self, syntax_ptr: AstPtr, e: ast::ForExpr) - /// ControlFlow::Continue(val) => val, /// ControlFlow::Break(residual) => /// // If there is an enclosing `try {...}`: + /// break 'catch_target Residual::into_try_type(residual), + /// // If there is an enclosing `try bikeshed Ty {...}`: /// break 'catch_target Try::from_residual(residual), /// // Otherwise: /// return Try::from_residual(residual), @@ -1873,7 +1918,6 @@ fn collect_try_operator(&mut self, syntax_ptr: AstPtr, e: ast::TryExp let try_branch = self.lang_path(lang_items.TryTraitBranch); let cf_continue = self.lang_path(lang_items.ControlFlowContinue); let cf_break = self.lang_path(lang_items.ControlFlowBreak); - let try_from_residual = self.lang_path(lang_items.TryTraitFromResidual); let operand = self.collect_expr_opt(e.expr()); let try_branch = self.alloc_expr(try_branch.map_or(Expr::Missing, Expr::Path), syntax_ptr); let expr = self @@ -1910,13 +1954,23 @@ fn collect_try_operator(&mut self, syntax_ptr: AstPtr, e: ast::TryExp guard: None, expr: { let it = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr); - let callee = self - .alloc_expr(try_from_residual.map_or(Expr::Missing, Expr::Path), syntax_ptr); + let convert_fn = match self.current_try_block { + Some(TryBlock::Homogeneous { .. }) => { + self.lang_path(lang_items.ResidualIntoTryType) + } + Some(TryBlock::Heterogeneous { .. }) | None => { + self.lang_path(lang_items.TryTraitFromResidual) + } + }; + let callee = + self.alloc_expr(convert_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr); let result = self.alloc_expr(Expr::Call { callee, args: Box::new([it]) }, syntax_ptr); self.alloc_expr( - match self.current_try_block_label { - Some(label) => Expr::Break { expr: Some(result), label: Some(label) }, + match self.current_try_block { + Some( + TryBlock::Heterogeneous { label } | TryBlock::Homogeneous { label }, + ) => Expr::Break { expr: Some(result), label: Some(label) }, None => Expr::Return { expr: Some(result) }, }, syntax_ptr, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 9e1efb977786..1303773b59d4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -893,6 +893,24 @@ pub(crate) fn update_visibility_macros(&mut self, name: &Name, vis: Visibility) self.macros.get_mut(name).expect("tried to update visibility of non-existent macro"); res.vis = vis; } + + pub(crate) fn update_def_types(&mut self, name: &Name, def: ModuleDefId, vis: Visibility) { + let res = self.types.get_mut(name).expect("tried to update def of non-existent type"); + res.def = def; + res.vis = vis; + } + + pub(crate) fn update_def_values(&mut self, name: &Name, def: ModuleDefId, vis: Visibility) { + let res = self.values.get_mut(name).expect("tried to update def of non-existent value"); + res.def = def; + res.vis = vis; + } + + pub(crate) fn update_def_macros(&mut self, name: &Name, def: MacroId, vis: Visibility) { + let res = self.macros.get_mut(name).expect("tried to update def of non-existent macro"); + res.def = def; + res.vis = vis; + } } impl PerNs { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 51dd55301f44..fef92c89b145 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -456,6 +456,7 @@ pub fn from_symbol(symbol: &Symbol) -> Option { TryTraitFromOutput, sym::from_output, FunctionId; TryTraitBranch, sym::branch, FunctionId; TryTraitFromYeet, sym::from_yeet, FunctionId; + ResidualIntoTryType, sym::into_try_type, FunctionId; PointerLike, sym::pointer_like, TraitId; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 323060f61d15..f51524c1b551 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -1209,42 +1209,69 @@ fn push_res_and_update_glob_vis( // `ItemScope::push_res_with_import()`. if let Some(def) = defs.types && let Some(prev_def) = prev_defs.types - && def.def == prev_def.def - && self.from_glob_import.contains_type(module_id, name.clone()) - && def.vis != prev_def.vis - && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis) { - changed = true; - // This import is being handled here, don't pass it down to - // `ItemScope::push_res_with_import()`. - defs.types = None; - self.def_map.modules[module_id].scope.update_visibility_types(name, def.vis); + if def.def == prev_def.def + && self.from_glob_import.contains_type(module_id, name.clone()) + && def.vis != prev_def.vis + && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis) + { + changed = true; + // This import is being handled here, don't pass it down to + // `ItemScope::push_res_with_import()`. + defs.types = None; + self.def_map.modules[module_id].scope.update_visibility_types(name, def.vis); + } + // When the source module's definition changed (e.g., due to an explicit import + // shadowing a glob), propagate the new definition to modules that glob-import from it. + // We check that the previous definition came from the same glob import to avoid + // incorrectly overwriting definitions from different glob sources. + // + // Note this is not a perfect fix, but it makes + // https://github.com/rust-lang/rust-analyzer/issues/19224 work for now until we + // implement a proper glob graph + else if def.def != prev_def.def && prev_def.import == def_import_type { + changed = true; + defs.types = None; + self.def_map.modules[module_id].scope.update_def_types(name, def.def, def.vis); + } } if let Some(def) = defs.values && let Some(prev_def) = prev_defs.values - && def.def == prev_def.def - && self.from_glob_import.contains_value(module_id, name.clone()) - && def.vis != prev_def.vis - && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis) { - changed = true; - // See comment above. - defs.values = None; - self.def_map.modules[module_id].scope.update_visibility_values(name, def.vis); + if def.def == prev_def.def + && self.from_glob_import.contains_value(module_id, name.clone()) + && def.vis != prev_def.vis + && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis) + { + changed = true; + defs.values = None; + self.def_map.modules[module_id].scope.update_visibility_values(name, def.vis); + } else if def.def != prev_def.def + && prev_def.import.map(ImportOrExternCrate::from) == def_import_type + { + changed = true; + defs.values = None; + self.def_map.modules[module_id].scope.update_def_values(name, def.def, def.vis); + } } if let Some(def) = defs.macros && let Some(prev_def) = prev_defs.macros - && def.def == prev_def.def - && self.from_glob_import.contains_macro(module_id, name.clone()) - && def.vis != prev_def.vis - && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis) { - changed = true; - // See comment above. - defs.macros = None; - self.def_map.modules[module_id].scope.update_visibility_macros(name, def.vis); + if def.def == prev_def.def + && self.from_glob_import.contains_macro(module_id, name.clone()) + && def.vis != prev_def.vis + && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis) + { + changed = true; + defs.macros = None; + self.def_map.modules[module_id].scope.update_visibility_macros(name, def.vis); + } else if def.def != prev_def.def && prev_def.import == def_import_type { + changed = true; + defs.macros = None; + self.def_map.modules[module_id].scope.update_def_macros(name, def.def, def.vis); + } } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 6e4b96b05088..3029bca1d51f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -830,15 +830,18 @@ fn include_bytes_expand( span: Span, ) -> ExpandResult { // FIXME: actually read the file here if the user asked for macro expansion - let res = tt::TopSubtree::invisible_from_leaves( + let underscore = sym::underscore; + let zero = tt::Literal { + text_and_suffix: sym::_0_u8, span, - [tt::Leaf::Literal(tt::Literal { - text_and_suffix: Symbol::empty(), - span, - kind: tt::LitKind::ByteStrRaw(1), - suffix_len: 0, - })], - ); + kind: tt::LitKind::Integer, + suffix_len: 3, + }; + // We don't use a real length since we can't know the file length, so we use an underscore + // to infer it. + let res = quote! {span => + &[#zero; #underscore] + }; ExpandResult::ok(res) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs index f3e67d01e566..0c3c51366861 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs @@ -133,7 +133,7 @@ pub fn impl_trait<'db>( } } -#[salsa::tracked(returns(ref), unsafe(non_update_types))] +#[salsa::tracked(returns(ref))] pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates { let loc = impl_.loc(db); let generic_params = GenericParams::new(db, loc.adt.into()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 43b428c3fa51..4e77e8be364b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -12,7 +12,6 @@ use hir_def::{ FindPathConfig, GenericDefId, GenericParamId, HasModule, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, - db::DefDatabase, expr_store::{ExpressionStore, path::Path}, find_path::{self, PrefixKind}, hir::generics::{TypeOrConstParamData, TypeParamProvenance, WherePredicate}, @@ -100,6 +99,9 @@ pub struct HirFormatter<'a, 'db> { display_kind: DisplayKind, display_target: DisplayTarget, bounds_formatting_ctx: BoundsFormattingCtx<'db>, + /// Whether formatting `impl Trait1 + Trait2` or `dyn Trait1 + Trait2` needs parentheses around it, + /// for example when formatting `&(impl Trait1 + Trait2)`. + trait_bounds_need_parens: bool, } // FIXME: To consider, ref and dyn trait lifetimes can be omitted if they are `'_`, path args should @@ -331,6 +333,7 @@ fn display_source_code<'a>( show_container_bounds: false, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, bounds_formatting_ctx: Default::default(), + trait_bounds_need_parens: false, }) { Ok(()) => {} Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"), @@ -566,6 +569,7 @@ pub fn write_to(&self, f: &mut F) -> Result { show_container_bounds: self.show_container_bounds, display_lifetimes: self.display_lifetimes, bounds_formatting_ctx: Default::default(), + trait_bounds_need_parens: false, }) } @@ -612,7 +616,11 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { } } -fn write_projection<'db>(f: &mut HirFormatter<'_, 'db>, alias: &AliasTy<'db>) -> Result { +fn write_projection<'db>( + f: &mut HirFormatter<'_, 'db>, + alias: &AliasTy<'db>, + needs_parens_if_multi: bool, +) -> Result { if f.should_truncate() { return write!(f, "{TYPE_HINT_TRUNCATION}"); } @@ -650,6 +658,7 @@ fn write_projection<'db>(f: &mut HirFormatter<'_, 'db>, alias: &AliasTy<'db>) -> Either::Left(Ty::new_alias(f.interner, AliasTyKind::Projection, *alias)), &bounds, SizedByDefault::NotSized, + needs_parens_if_multi, ) }); } @@ -1056,7 +1065,7 @@ fn hir_fmt(&self, f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_, 'db>) return write!(f, "{TYPE_HINT_TRUNCATION}"); } - use TyKind; + let trait_bounds_need_parens = mem::replace(&mut f.trait_bounds_need_parens, false); match self.kind() { TyKind::Never => write!(f, "!")?, TyKind::Str => write!(f, "str")?, @@ -1077,103 +1086,34 @@ fn hir_fmt(&self, f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_, 'db>) c.hir_fmt(f)?; write!(f, "]")?; } - kind @ (TyKind::RawPtr(t, m) | TyKind::Ref(_, t, m)) => { - if let TyKind::Ref(l, _, _) = kind { - f.write_char('&')?; - if f.render_region(l) { - l.hir_fmt(f)?; - f.write_char(' ')?; - } + TyKind::Ref(l, t, m) => { + f.write_char('&')?; + if f.render_region(l) { + l.hir_fmt(f)?; + f.write_char(' ')?; + } + match m { + rustc_ast_ir::Mutability::Not => (), + rustc_ast_ir::Mutability::Mut => f.write_str("mut ")?, + } + + f.trait_bounds_need_parens = true; + t.hir_fmt(f)?; + f.trait_bounds_need_parens = false; + } + TyKind::RawPtr(t, m) => { + write!( + f, + "*{}", match m { - rustc_ast_ir::Mutability::Not => (), - rustc_ast_ir::Mutability::Mut => f.write_str("mut ")?, + rustc_ast_ir::Mutability::Not => "const ", + rustc_ast_ir::Mutability::Mut => "mut ", } - } else { - write!( - f, - "*{}", - match m { - rustc_ast_ir::Mutability::Not => "const ", - rustc_ast_ir::Mutability::Mut => "mut ", - } - )?; - } + )?; - // FIXME: all this just to decide whether to use parentheses... - let (preds_to_print, has_impl_fn_pred) = match t.kind() { - TyKind::Dynamic(bounds, region) => { - let contains_impl_fn = - bounds.iter().any(|bound| match bound.skip_binder() { - ExistentialPredicate::Trait(trait_ref) => { - let trait_ = trait_ref.def_id.0; - fn_traits(f.lang_items()).any(|it| it == trait_) - } - _ => false, - }); - let render_lifetime = f.render_region(region); - (bounds.len() + render_lifetime as usize, contains_impl_fn) - } - TyKind::Alias(AliasTyKind::Opaque, ty) => { - let opaque_ty_id = match ty.def_id { - SolverDefId::InternedOpaqueTyId(id) => id, - _ => unreachable!(), - }; - let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id); - if let ImplTraitId::ReturnTypeImplTrait(func, _) = impl_trait_id { - let data = impl_trait_id.predicates(db); - let bounds = - || data.iter_instantiated_copied(f.interner, ty.args.as_slice()); - let mut len = bounds().count(); - - // Don't count Sized but count when it absent - // (i.e. when explicit ?Sized bound is set). - let default_sized = SizedByDefault::Sized { anchor: func.krate(db) }; - let sized_bounds = bounds() - .filter(|b| { - matches!( - b.kind().skip_binder(), - ClauseKind::Trait(trait_ref) - if default_sized.is_sized_trait( - trait_ref.def_id().0, - db, - ), - ) - }) - .count(); - match sized_bounds { - 0 => len += 1, - _ => { - len = len.saturating_sub(sized_bounds); - } - } - - let contains_impl_fn = bounds().any(|bound| { - if let ClauseKind::Trait(trait_ref) = bound.kind().skip_binder() { - let trait_ = trait_ref.def_id().0; - fn_traits(f.lang_items()).any(|it| it == trait_) - } else { - false - } - }); - (len, contains_impl_fn) - } else { - (0, false) - } - } - _ => (0, false), - }; - - if has_impl_fn_pred && preds_to_print <= 2 { - return t.hir_fmt(f); - } - - if preds_to_print > 1 { - write!(f, "(")?; - t.hir_fmt(f)?; - write!(f, ")")?; - } else { - t.hir_fmt(f)?; - } + f.trait_bounds_need_parens = true; + t.hir_fmt(f)?; + f.trait_bounds_need_parens = false; } TyKind::Tuple(tys) => { if tys.len() == 1 { @@ -1328,7 +1268,9 @@ fn hir_fmt(&self, f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_, 'db>) hir_fmt_generics(f, parameters.as_slice(), Some(def.def_id().0.into()), None)?; } - TyKind::Alias(AliasTyKind::Projection, alias_ty) => write_projection(f, &alias_ty)?, + TyKind::Alias(AliasTyKind::Projection, alias_ty) => { + write_projection(f, &alias_ty, trait_bounds_need_parens)? + } TyKind::Foreign(alias) => { let type_alias = db.type_alias_signature(alias.0); f.start_location_link(alias.0.into()); @@ -1363,6 +1305,7 @@ fn hir_fmt(&self, f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_, 'db>) Either::Left(*self), &bounds, SizedByDefault::Sized { anchor: krate }, + trait_bounds_need_parens, )?; } TyKind::Closure(id, substs) => { @@ -1525,6 +1468,7 @@ fn hir_fmt(&self, f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_, 'db>) Either::Left(*self), &bounds, SizedByDefault::Sized { anchor: krate }, + trait_bounds_need_parens, )?; } }, @@ -1567,6 +1511,7 @@ fn hir_fmt(&self, f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_, 'db>) Either::Left(*self), &bounds_to_display, SizedByDefault::NotSized, + trait_bounds_need_parens, )?; } TyKind::Error(_) => { @@ -1806,11 +1751,11 @@ pub enum SizedByDefault { } impl SizedByDefault { - fn is_sized_trait(self, trait_: TraitId, db: &dyn DefDatabase) -> bool { + fn is_sized_trait(self, trait_: TraitId, interner: DbInterner<'_>) -> bool { match self { Self::NotSized => false, - Self::Sized { anchor } => { - let sized_trait = hir_def::lang_item::lang_items(db, anchor).Sized; + Self::Sized { .. } => { + let sized_trait = interner.lang_items().Sized; Some(trait_) == sized_trait } } @@ -1823,16 +1768,62 @@ pub fn write_bounds_like_dyn_trait_with_prefix<'db>( this: Either, Region<'db>>, predicates: &[Clause<'db>], default_sized: SizedByDefault, + needs_parens_if_multi: bool, ) -> Result { + let needs_parens = + needs_parens_if_multi && trait_bounds_need_parens(f, this, predicates, default_sized); + if needs_parens { + write!(f, "(")?; + } write!(f, "{prefix}")?; if !predicates.is_empty() || predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. }) { write!(f, " ")?; - write_bounds_like_dyn_trait(f, this, predicates, default_sized) - } else { - Ok(()) + write_bounds_like_dyn_trait(f, this, predicates, default_sized)?; } + if needs_parens { + write!(f, ")")?; + } + Ok(()) +} + +fn trait_bounds_need_parens<'db>( + f: &mut HirFormatter<'_, 'db>, + this: Either, Region<'db>>, + predicates: &[Clause<'db>], + default_sized: SizedByDefault, +) -> bool { + // This needs to be kept in sync with `write_bounds_like_dyn_trait()`. + let mut distinct_bounds = 0usize; + let mut is_sized = false; + for p in predicates { + match p.kind().skip_binder() { + ClauseKind::Trait(trait_ref) => { + let trait_ = trait_ref.def_id().0; + if default_sized.is_sized_trait(trait_, f.interner) { + is_sized = true; + if matches!(default_sized, SizedByDefault::Sized { .. }) { + // Don't print +Sized, but rather +?Sized if absent. + continue; + } + } + + distinct_bounds += 1; + } + ClauseKind::TypeOutlives(to) if Either::Left(to.0) == this => distinct_bounds += 1, + ClauseKind::RegionOutlives(lo) if Either::Right(lo.0) == this => distinct_bounds += 1, + _ => {} + } + } + + if let SizedByDefault::Sized { .. } = default_sized + && !is_sized + { + distinct_bounds += 1; + } + + distinct_bounds > 1 } fn write_bounds_like_dyn_trait<'db>( @@ -1855,7 +1846,7 @@ fn write_bounds_like_dyn_trait<'db>( match p.kind().skip_binder() { ClauseKind::Trait(trait_ref) => { let trait_ = trait_ref.def_id().0; - if default_sized.is_sized_trait(trait_, f.db) { + if default_sized.is_sized_trait(trait_, f.interner) { is_sized = true; if matches!(default_sized, SizedByDefault::Sized { .. }) { // Don't print +Sized, but rather +?Sized if absent. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 5789bf02a42e..386556b15684 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -831,6 +831,8 @@ fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty<'db> { let mut ordered_associated_types = vec![]; if let Some(principal_trait) = principal { + // Generally we should not elaborate in lowering as this can lead to cycles, but + // here rustc cycles as well. for clause in elaborate::elaborate( interner, [Clause::upcast_from( @@ -1897,7 +1899,7 @@ impl<'db> GenericPredicates { /// Resolve the where clause(s) of an item with generics. /// /// Diagnostics are computed only for this item's predicates, not for parents. - #[salsa::tracked(returns(ref))] + #[salsa::tracked(returns(ref), cycle_result=generic_predicates_cycle_result)] pub fn query_with_diagnostics( db: &'db dyn HirDatabase, def: GenericDefId, @@ -1906,6 +1908,20 @@ pub fn query_with_diagnostics( } } +/// A cycle can occur from malformed code. +fn generic_predicates_cycle_result( + _db: &dyn HirDatabase, + _: salsa::Id, + _def: GenericDefId, +) -> (GenericPredicates, Diagnostics) { + ( + GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( + Clauses::default().store(), + )), + None, + ) +} + impl GenericPredicates { #[inline] pub(crate) fn from_explicit_own_predicates( @@ -2590,11 +2606,13 @@ pub(crate) fn associated_type_by_name_including_super_traits<'db>( ) -> Option<(TraitRef<'db>, TypeAliasId)> { let module = trait_ref.def_id.0.module(db); let interner = DbInterner::new_with(db, module.krate(db)); - rustc_type_ir::elaborate::supertraits(interner, Binder::dummy(trait_ref)).find_map(|t| { - let trait_id = t.as_ref().skip_binder().def_id.0; - let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?; - Some((t.skip_binder(), assoc_type)) - }) + all_supertraits_trait_refs(db, trait_ref.def_id.0) + .map(|t| t.instantiate(interner, trait_ref.args)) + .find_map(|t| { + let trait_id = t.def_id.0; + let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?; + Some((t, assoc_type)) + }) } pub fn associated_type_shorthand_candidates( @@ -2723,3 +2741,96 @@ fn named_associated_type_shorthand_candidates<'db, R>( _ => None, } } + +/// During lowering, elaborating supertraits can cause cycles. To avoid that, we have a separate query +/// to only collect supertraits. +/// +/// Technically, it is possible to avoid even more cycles by only collecting the `TraitId` of supertraits +/// without their args. However rustc doesn't do that, so we don't either. +pub(crate) fn all_supertraits_trait_refs( + db: &dyn HirDatabase, + trait_: TraitId, +) -> impl ExactSizeIterator>> { + let interner = DbInterner::new_no_crate(db); + return all_supertraits_trait_refs_query(db, trait_).iter().map(move |trait_ref| { + trait_ref.get_with(|(trait_, args)| { + TraitRef::new_from_args(interner, (*trait_).into(), args.as_ref()) + }) + }); + + #[salsa_macros::tracked(returns(deref), cycle_result = all_supertraits_trait_refs_cycle_result)] + pub(crate) fn all_supertraits_trait_refs_query( + db: &dyn HirDatabase, + trait_: TraitId, + ) -> Box<[StoredEarlyBinder<(TraitId, StoredGenericArgs)>]> { + let resolver = trait_.resolver(db); + let signature = db.trait_signature(trait_); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &signature.store, + trait_.into(), + LifetimeElisionKind::AnonymousReportError, + ); + let interner = ctx.interner; + + let self_param_ty = Ty::new_param( + interner, + TypeParamId::from_unchecked(TypeOrConstParamId { + parent: trait_.into(), + local_id: Idx::from_raw(la_arena::RawIdx::from_u32(0)), + }), + 0, + ); + + let mut supertraits = FxHashSet::default(); + supertraits.insert(StoredEarlyBinder::bind(( + trait_, + GenericArgs::identity_for_item(interner, trait_.into()).store(), + ))); + + for pred in signature.generic_params.where_predicates() { + let WherePredicate::TypeBound { target, bound } = pred else { + continue; + }; + let target = &signature.store[*target]; + if let TypeRef::TypeParam(param_id) = target + && param_id.local_id().into_raw().into_u32() == 0 + { + // This is `Self`. + } else if let TypeRef::Path(path) = target + && path.is_self_type() + { + // Also `Self`. + } else { + // Not `Self`! + continue; + } + + ctx.lower_type_bound(bound, self_param_ty, true).for_each(|(clause, _)| { + if let ClauseKind::Trait(trait_ref) = clause.kind().skip_binder() { + supertraits.extend( + all_supertraits_trait_refs(db, trait_ref.trait_ref.def_id.0).map(|t| { + let trait_ref = t.instantiate(interner, trait_ref.trait_ref.args); + StoredEarlyBinder::bind((trait_ref.def_id.0, trait_ref.args.store())) + }), + ); + } + }); + } + + Box::from_iter(supertraits) + } + + pub(crate) fn all_supertraits_trait_refs_cycle_result( + db: &dyn HirDatabase, + _: salsa::Id, + trait_: TraitId, + ) -> Box<[StoredEarlyBinder<(TraitId, StoredGenericArgs)>]> { + let interner = DbInterner::new_no_crate(db); + Box::new([StoredEarlyBinder::bind(( + trait_, + GenericArgs::identity_for_item(interner, trait_.into()).store(), + ))]) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 36630ab587cd..438699b40983 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -608,7 +608,7 @@ trait Foo {} fn test(f: impl Foo, g: &(impl Foo + ?Sized)) { let _: &dyn Foo = &f; let _: &dyn Foo = g; - //^ expected &'? (dyn Foo + 'static), got &'? impl Foo + ?Sized + //^ expected &'? (dyn Foo + 'static), got &'? (impl Foo + ?Sized) } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs index dc3869930dcf..37da7fc87563 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs @@ -111,7 +111,7 @@ fn test( b; //^ impl Foo c; - //^ &impl Foo + ?Sized + //^ &(impl Foo + ?Sized) d; //^ S ref_any; @@ -192,7 +192,7 @@ fn test( b; //^ fn(impl Foo) -> impl Foo c; -} //^ fn(&impl Foo + ?Sized) -> &impl Foo + ?Sized +} //^ fn(&(impl Foo + ?Sized)) -> &(impl Foo + ?Sized) "#, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 4f1480c39366..3b5b4e4fa540 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2363,6 +2363,7 @@ fn test() { } "#, expect![[r#" + 46..49 'Foo': Foo 93..97 'self': Foo 108..125 '{ ... }': usize 118..119 'N': usize @@ -2645,3 +2646,45 @@ fn bar(v: Self::Assoc) { "#, ); } + +#[test] +fn issue_21560() { + check_no_mismatches( + r#" +mod bindings { + use super::*; + pub type HRESULT = i32; +} +use bindings::*; + + +mod error { + use super::*; + pub fn nonzero_hresult(hr: HRESULT) -> crate::HRESULT { + hr + } +} +pub use error::*; + +mod hresult { + use super::*; + pub struct HRESULT(pub i32); +} +pub use hresult::HRESULT; + + "#, + ); +} + +#[test] +fn regression_21577() { + check_no_mismatches( + r#" +pub trait FilterT = Self> { + type V; + + fn foo() {} +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 98503452d348..7d4f04268af9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -2152,10 +2152,11 @@ async fn main() { let z: core::ops::ControlFlow<(), _> = try { () }; let w = const { 92 }; let t = 'a: { 92 }; + let u = try bikeshed core::ops::ControlFlow<(), _> { () }; } "#, expect![[r#" - 16..193 '{ ...2 }; }': () + 16..256 '{ ...) }; }': () 26..27 'x': i32 30..43 'unsafe { 92 }': i32 39..41 '92': i32 @@ -2176,6 +2177,13 @@ async fn main() { 176..177 't': i32 180..190 ''a: { 92 }': i32 186..188 '92': i32 + 200..201 'u': ControlFlow<(), ()> + 204..253 'try bi...{ () }': ControlFlow<(), ()> + 204..253 'try bi...{ () }': fn from_output>( as Try>::Output) -> ControlFlow<(), ()> + 204..253 'try bi...{ () }': ControlFlow<(), ()> + 204..253 'try bi...{ () }': ControlFlow<(), ()> + 204..253 'try bi...{ () }': ControlFlow<(), ()> + 249..251 '()': () "#]], ) } @@ -4056,3 +4064,13 @@ fn foo() { "#]], ); } + +#[test] +fn include_bytes_len_mismatch() { + check_no_mismatches( + r#" +//- minicore: include_bytes +static S: &[u8; 158] = include_bytes!("/foo/bar/baz.txt"); + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 390553c0d7a9..cdf7b40003b5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -219,14 +219,16 @@ fn test() { #[test] fn infer_try_block() { - // FIXME: We should test more cases, but it currently doesn't work, since - // our labeled block type inference is broken. check_types( r#" -//- minicore: try, option +//- minicore: try, option, result, from fn test() { let x: Option<_> = try { Some(2)?; }; //^ Option<()> + let homogeneous = try { Ok::<(), u32>(())?; "hi" }; + //^^^^^^^^^^^ Result<&'? str, u32> + let heterogeneous = try bikeshed Result<_, u64> { 1 }; + //^^^^^^^^^^^^^ Result } "#, ); @@ -4819,7 +4821,7 @@ fn allowed3(baz: impl Baz>) {} 431..433 '{}': () 447..450 'baz': impl Baz 480..482 '{}': () - 500..503 'baz': impl Baz + 500..503 'baz': impl Baz 544..546 '{}': () 560..563 'baz': impl Baz> 598..600 '{}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 7dd73f1e7aa0..148300deb875 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -22,6 +22,7 @@ TargetFeatures, db::HirDatabase, layout::{Layout, TagEncoding}, + lower::all_supertraits_trait_refs, mir::pad16, }; @@ -62,23 +63,13 @@ pub(crate) fn fn_traits(lang_items: &LangItems) -> impl Iterator /// Returns an iterator over the whole super trait hierarchy (including the /// trait itself). -pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> { - // we need to take care a bit here to avoid infinite loops in case of cycles - // (i.e. if we have `trait A: B; trait B: A;`) - - let mut result = smallvec![trait_]; - let mut i = 0; - while let Some(&t) = result.get(i) { - // yeah this is quadratic, but trait hierarchies should be flat - // enough that this doesn't matter - direct_super_traits_cb(db, t, |tt| { - if !result.contains(&tt) { - result.push(tt); - } - }); - i += 1; - } - result +pub fn all_super_traits(db: &dyn HirDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> { + let mut supertraits = all_supertraits_trait_refs(db, trait_) + .map(|trait_ref| trait_ref.skip_binder().def_id.0) + .collect::>(); + supertraits.sort_unstable(); + supertraits.dedup(); + supertraits } fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(TraitId)) { diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 1f9af564c359..b4440dfa1826 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -587,6 +587,7 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { Either::Left(ty), &predicates, SizedByDefault::Sized { anchor: krate }, + false, ); } }, @@ -614,6 +615,7 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { Either::Left(ty), &predicates, default_sized, + false, )?; } Ok(()) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 252d71fb80a4..4b615665167c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4233,6 +4233,10 @@ pub fn module(self, db: &dyn HirDatabase) -> Module { self.parent(db).module(db) } + pub fn as_id(self) -> u32 { + self.binding_id.into_raw().into_u32() + } + pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { let def = self.parent; let infer = InferenceResult::for_body(db, def); diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index 05a89e76529b..8622aa1378b3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -18,7 +18,6 @@ use itertools::Itertools; use rustc_hash::FxHashSet; use rustc_type_ir::inherent::Ty as _; -use span::Edition; use crate::{ Adt, AssocItem, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, ModuleDef, ScopeDef, @@ -367,7 +366,11 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( let ret_ty = it.ret_type_with_args(db, generics.iter().cloned()); // Filter out private and unsafe functions if !it.is_visible_from(db, module) - || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME) + || it.is_unsafe_to_call( + db, + None, + crate::Crate::from(ctx.scope.resolver().krate()).edition(db), + ) || it.is_unstable(db) || ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() @@ -473,7 +476,11 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( // Filter out private and unsafe functions if !it.is_visible_from(db, module) - || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME) + || it.is_unsafe_to_call( + db, + None, + crate::Crate::from(ctx.scope.resolver().krate()).edition(db), + ) || it.is_unstable(db) { return None; @@ -667,7 +674,11 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( // Filter out private and unsafe functions if !it.is_visible_from(db, module) - || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME) + || it.is_unsafe_to_call( + db, + None, + crate::Crate::from(ctx.scope.resolver().krate()).edition(db), + ) || it.is_unstable(db) { return None; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index cc2bf8174941..2694910aa609 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -155,6 +155,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< &scope, mod_path_to_ast(&import_path, edition), &ctx.config.insert_use, + edition, ); }, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index ca142332d97e..5a223e11301c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -132,7 +132,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) ); } - if block.try_token().is_none() + if block.try_block_modifier().is_none() && block.unsafe_token().is_none() && block.label().is_none() && block.const_token().is_none() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index f2363c6f7ba2..124ef509fb89 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -859,7 +859,7 @@ fn analyze_container<'db>( ast::BlockExpr(block_expr) => { let (constness, block) = match block_expr.modifier() { Some(ast::BlockModifier::Const(_)) => (true, block_expr), - Some(ast::BlockModifier::Try(_)) => (false, block_expr), + Some(ast::BlockModifier::Try { .. }) => (false, block_expr), Some(ast::BlockModifier::Label(label)) if label.lifetime().is_some() => (false, block_expr), _ => continue, }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index bd66c02b4150..ded3b0f5acb2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -1147,14 +1147,7 @@ fn maybe_displayed_type( if ty.is_reference() || ty.is_mutable_reference() { let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate()); convert_reference_type(ty.strip_references(), ctx.db(), famous_defs) - .map(|conversion| { - conversion - .convert_type( - ctx.db(), - target_module.krate(ctx.db()).to_display_target(ctx.db()), - ) - .to_string() - }) + .map(|conversion| conversion.convert_type(ctx.db(), target_module).to_string()) .or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok()) } else { ty.display_source_code(ctx.db(), target_module.into(), true).ok() @@ -3187,6 +3180,28 @@ fn main() { r#" fn main() { s.self$0(); +} + "#, + ); + } + + #[test] + fn regression_21288() { + check_assist( + generate_function, + r#" +//- minicore: copy +fn foo() { + $0bar(&|x| true) +} + "#, + r#" +fn foo() { + bar(&|x| true) +} + +fn bar(arg: impl Fn(_) -> bool) { + ${0:todo!()} } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index e42d0ed1b00b..73e93a3fbf52 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs @@ -226,15 +226,15 @@ fn generate_getter_from_info( ) } else { (|| { - let krate = ctx.sema.scope(record_field_info.field_ty.syntax())?.krate(); - let famous_defs = &FamousDefs(&ctx.sema, krate); + let module = ctx.sema.scope(record_field_info.field_ty.syntax())?.module(); + let famous_defs = &FamousDefs(&ctx.sema, module.krate(ctx.db())); ctx.sema .resolve_type(&record_field_info.field_ty) .and_then(|ty| convert_reference_type(ty, ctx.db(), famous_defs)) .map(|conversion| { cov_mark::hit!(convert_reference_type); ( - conversion.convert_type(ctx.db(), krate.to_display_target(ctx.db())), + conversion.convert_type(ctx.db(), module), conversion.getter(record_field_info.field_name.to_string()), ) }) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs index 84f02bdfdba6..fdc5a0fbda35 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs @@ -1,6 +1,7 @@ -use itertools::Itertools; +use itertools::{Itertools, chain}; use syntax::{ SyntaxKind::WHITESPACE, + TextRange, ast::{ AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat, edit::AstNodeEdit, make, prec::ExprPrecedence, syntax_factory::SyntaxFactory, @@ -44,13 +45,26 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) cov_mark::hit!(move_guard_inapplicable_in_arm_body); return None; } - let space_before_guard = guard.syntax().prev_sibling_or_token(); + let rest_arms = rest_arms(&match_arm, ctx.selection_trimmed())?; + let space_before_delete = chain( + guard.syntax().prev_sibling_or_token(), + rest_arms.iter().filter_map(|it| it.syntax().prev_sibling_or_token()), + ); let space_after_arrow = match_arm.fat_arrow_token()?.next_sibling_or_token(); - let guard_condition = guard.condition()?.reset_indent(); let arm_expr = match_arm.expr()?; - let then_branch = crate::utils::wrap_block(&arm_expr); - let if_expr = make::expr_if(guard_condition, then_branch, None).indent(arm_expr.indent_level()); + let if_branch = chain([&match_arm], &rest_arms) + .rfold(None, |else_branch, arm| { + if let Some(guard) = arm.guard() { + let then_branch = crate::utils::wrap_block(&arm.expr()?); + let guard_condition = guard.condition()?.reset_indent(); + Some(make::expr_if(guard_condition, then_branch, else_branch).into()) + } else { + arm.expr().map(|it| crate::utils::wrap_block(&it).into()) + } + })? + .indent(arm_expr.indent_level()); + let ElseBranch::IfExpr(if_expr) = if_branch else { return None }; let target = guard.syntax().text_range(); acc.add( @@ -59,10 +73,13 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) target, |builder| { let mut edit = builder.make_editor(match_arm.syntax()); - if let Some(element) = space_before_guard - && element.kind() == WHITESPACE - { - edit.delete(element); + for element in space_before_delete { + if element.kind() == WHITESPACE { + edit.delete(element); + } + } + for rest_arm in &rest_arms { + edit.delete(rest_arm.syntax()); } if let Some(element) = space_after_arrow && element.kind() == WHITESPACE @@ -221,6 +238,25 @@ pub(crate) fn move_arm_cond_to_match_guard( ) } +fn rest_arms(match_arm: &MatchArm, selection: TextRange) -> Option> { + match_arm + .parent_match() + .match_arm_list()? + .arms() + .skip_while(|it| it != match_arm) + .skip(1) + .take_while(move |it| { + selection.is_empty() || crate::utils::is_selected(it, selection, false) + }) + .take_while(move |it| { + it.pat() + .zip(match_arm.pat()) + .is_some_and(|(a, b)| a.syntax().text() == b.syntax().text()) + }) + .collect::>() + .into() +} + // Parses an if-else-if chain to get the conditions and the then branches until we encounter an else // branch or the end. fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Expr, BlockExpr)>, Option)> { @@ -344,6 +380,115 @@ fn main() { ); } + #[test] + fn move_multiple_guard_to_arm_body_works() { + check_assist( + move_guard_to_arm_body, + r#" +fn main() { + match 92 { + x @ 0..30 $0if x % 3 == 0 => false, + x @ 0..30 if x % 2 == 0 => true, + _ => false + } +} +"#, + r#" +fn main() { + match 92 { + x @ 0..30 => if x % 3 == 0 { + false + } else if x % 2 == 0 { + true + }, + _ => false + } +} +"#, + ); + + check_assist( + move_guard_to_arm_body, + r#" +fn main() { + match 92 { + x @ 0..30 $0if x % 3 == 0 => false, + x @ 0..30 if x % 2 == 0 => true, + x @ 0..30 => false, + _ => true + } +} +"#, + r#" +fn main() { + match 92 { + x @ 0..30 => if x % 3 == 0 { + false + } else if x % 2 == 0 { + true + } else { + false + }, + _ => true + } +} +"#, + ); + + check_assist( + move_guard_to_arm_body, + r#" +fn main() { + match 92 { + x @ 0..30 if x % 3 == 0 => false, + x @ 0..30 $0if x % 2 == 0$0 => true, + x @ 0..30 => false, + _ => true + } +} +"#, + r#" +fn main() { + match 92 { + x @ 0..30 if x % 3 == 0 => false, + x @ 0..30 => if x % 2 == 0 { + true + }, + x @ 0..30 => false, + _ => true + } +} +"#, + ); + + check_assist( + move_guard_to_arm_body, + r#" +fn main() { + match 92 { + x @ 0..30 $0if x % 3 == 0 => false, + x @ 0..30 $0if x % 2 == 0 => true, + x @ 0..30 => false, + _ => true + } +} +"#, + r#" +fn main() { + match 92 { + x @ 0..30 => if x % 3 == 0 { + false + } else if x % 2 == 0 { + true + }, + x @ 0..30 => false, + _ => true + } +} +"#, + ); + } + #[test] fn move_guard_to_block_arm_body_works() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 4b8c19305793..5e08cba8e293 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -4,8 +4,7 @@ pub(crate) use gen_trait_fn_body::gen_trait_fn_body; use hir::{ - DisplayTarget, HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution, - Semantics, + HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution, Semantics, db::{ExpandDatabase, HirDatabase}, }; use ide_db::{ @@ -836,13 +835,12 @@ enum ReferenceConversionType { } impl<'db> ReferenceConversion<'db> { - pub(crate) fn convert_type( - &self, - db: &'db dyn HirDatabase, - display_target: DisplayTarget, - ) -> ast::Type { + pub(crate) fn convert_type(&self, db: &'db dyn HirDatabase, module: hir::Module) -> ast::Type { let ty = match self.conversion { - ReferenceConversionType::Copy => self.ty.display(db, display_target).to_string(), + ReferenceConversionType::Copy => self + .ty + .display_source_code(db, module.into(), true) + .unwrap_or_else(|_| "_".to_owned()), ReferenceConversionType::AsRefStr => "&str".to_owned(), ReferenceConversionType::AsRefSlice => { let type_argument_name = self @@ -850,8 +848,8 @@ pub(crate) fn convert_type( .type_arguments() .next() .unwrap() - .display(db, display_target) - .to_string(); + .display_source_code(db, module.into(), true) + .unwrap_or_else(|_| "_".to_owned()); format!("&[{type_argument_name}]") } ReferenceConversionType::Dereferenced => { @@ -860,8 +858,8 @@ pub(crate) fn convert_type( .type_arguments() .next() .unwrap() - .display(db, display_target) - .to_string(); + .display_source_code(db, module.into(), true) + .unwrap_or_else(|_| "_".to_owned()); format!("&{type_argument_name}") } ReferenceConversionType::Option => { @@ -870,16 +868,22 @@ pub(crate) fn convert_type( .type_arguments() .next() .unwrap() - .display(db, display_target) - .to_string(); + .display_source_code(db, module.into(), true) + .unwrap_or_else(|_| "_".to_owned()); format!("Option<&{type_argument_name}>") } ReferenceConversionType::Result => { let mut type_arguments = self.ty.type_arguments(); - let first_type_argument_name = - type_arguments.next().unwrap().display(db, display_target).to_string(); - let second_type_argument_name = - type_arguments.next().unwrap().display(db, display_target).to_string(); + let first_type_argument_name = type_arguments + .next() + .unwrap() + .display_source_code(db, module.into(), true) + .unwrap_or_else(|_| "_".to_owned()); + let second_type_argument_name = type_arguments + .next() + .unwrap() + .display_source_code(db, module.into(), true) + .unwrap_or_else(|_| "_".to_owned()); format!("Result<&{first_type_argument_name}, &{second_type_argument_name}>") } }; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 7f67ef848ece..cffc44f8afb3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -151,6 +151,10 @@ pub(crate) fn complete_postfix( .add_to(acc, ctx.db); } }, + _ if is_in_cond => { + postfix_snippet("let", "let", &format!("let $1 = {receiver_text}")) + .add_to(acc, ctx.db); + } _ if matches!(second_ancestor.kind(), STMT_LIST | EXPR_STMT) => { postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")) .add_to(acc, ctx.db); @@ -253,7 +257,6 @@ pub(crate) fn complete_postfix( &format!("while {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); - postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db); } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() && receiver_ty.impls_trait(ctx.db, trait_, &[]) { @@ -266,6 +269,10 @@ pub(crate) fn complete_postfix( } } + if receiver_ty.is_bool() || receiver_ty.is_unknown() { + postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db); + } + let block_should_be_wrapped = if let ast::Expr::BlockExpr(block) = dot_receiver { block.modifier().is_some() || !block.is_standalone() } else { @@ -585,6 +592,31 @@ fn main() { ); } + #[test] + fn postfix_completion_works_in_if_condition() { + check( + r#" +fn foo(cond: bool) { + if cond.$0 +} +"#, + expect![[r#" + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn not !expr + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); + } + #[test] fn postfix_type_filtering() { check( @@ -744,6 +776,25 @@ fn main() { ); } + #[test] + fn iflet_fallback_cond() { + check_edit( + "let", + r#" +fn main() { + let bar = 2; + if bar.$0 +} +"#, + r#" +fn main() { + let bar = 2; + if let $1 = bar +} +"#, + ); + } + #[test] fn option_letelse() { check_edit( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index 4444ef5d81d5..db1d599d550d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -146,9 +146,14 @@ pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { insert_use_with_alias_option(scope, path, cfg, None); } -pub fn insert_use_as_alias(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { +pub fn insert_use_as_alias( + scope: &ImportScope, + path: ast::Path, + cfg: &InsertUseConfig, + edition: span::Edition, +) { let text: &str = "use foo as _"; - let parse = syntax::SourceFile::parse(text, span::Edition::CURRENT_FIXME); + let parse = syntax::SourceFile::parse(text, edition); let node = parse .tree() .syntax() diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index 94ecf6a02ddc..e30b21c139fa 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -49,7 +49,7 @@ pub fn is_closure_or_blk_with_modif(expr: &ast::Expr) -> bool { block_expr.modifier(), Some( ast::BlockModifier::Async(_) - | ast::BlockModifier::Try(_) + | ast::BlockModifier::Try { .. } | ast::BlockModifier::Const(_) ) ) @@ -148,7 +148,7 @@ pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) { block_expr.modifier(), Some( ast::BlockModifier::Async(_) - | ast::BlockModifier::Try(_) + | ast::BlockModifier::Try { .. } | ast::BlockModifier::Const(_) ) ) @@ -291,7 +291,7 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) { match b.modifier() { Some( ast::BlockModifier::Async(_) - | ast::BlockModifier::Try(_) + | ast::BlockModifier::Try { .. } | ast::BlockModifier::Const(_), ) => return cb(expr), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index b8b9a7a76816..5d1e876ea298 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -206,17 +206,18 @@ pub fn try_for_variable( expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>, ) -> Option { + let edition = sema.scope(expr.syntax())?.krate().edition(sema.db); // `from_param` does not benefit from stripping it need the largest // context possible so we check firstmost - if let Some(name) = from_param(expr, sema) { + if let Some(name) = from_param(expr, sema, edition) { return Some(self.suggest_name(&name)); } let mut next_expr = Some(expr.clone()); while let Some(expr) = next_expr { - let name = from_call(&expr) - .or_else(|| from_type(&expr, sema)) - .or_else(|| from_field_name(&expr)); + let name = from_call(&expr, edition) + .or_else(|| from_type(&expr, sema, edition)) + .or_else(|| from_field_name(&expr, edition)); if let Some(name) = name { return Some(self.suggest_name(&name)); } @@ -270,7 +271,7 @@ fn split_numeric_suffix(name: &str) -> (&str, Option) { } } -fn normalize(name: &str) -> Option { +fn normalize(name: &str, edition: syntax::Edition) -> Option { let name = to_lower_snake_case(name).to_smolstr(); if USELESS_NAMES.contains(&name.as_str()) { @@ -281,16 +282,16 @@ fn normalize(name: &str) -> Option { return None; } - if !is_valid_name(&name) { + if !is_valid_name(&name, edition) { return None; } Some(name) } -fn is_valid_name(name: &str) -> bool { +fn is_valid_name(name: &str, edition: syntax::Edition) -> bool { matches!( - super::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name), + super::LexedStr::single_token(edition, name), Some((syntax::SyntaxKind::IDENT, _error)) ) } @@ -304,11 +305,11 @@ fn is_useless_method(method: &ast::MethodCallExpr) -> bool { } } -fn from_call(expr: &ast::Expr) -> Option { - from_func_call(expr).or_else(|| from_method_call(expr)) +fn from_call(expr: &ast::Expr, edition: syntax::Edition) -> Option { + from_func_call(expr, edition).or_else(|| from_method_call(expr, edition)) } -fn from_func_call(expr: &ast::Expr) -> Option { +fn from_func_call(expr: &ast::Expr, edition: syntax::Edition) -> Option { let call = match expr { ast::Expr::CallExpr(call) => call, _ => return None, @@ -318,10 +319,10 @@ fn from_func_call(expr: &ast::Expr) -> Option { _ => return None, }; let ident = func.path()?.segment()?.name_ref()?.ident_token()?; - normalize(ident.text()) + normalize(ident.text(), edition) } -fn from_method_call(expr: &ast::Expr) -> Option { +fn from_method_call(expr: &ast::Expr, edition: syntax::Edition) -> Option { let method = match expr { ast::Expr::MethodCallExpr(call) => call, _ => return None, @@ -340,10 +341,14 @@ fn from_method_call(expr: &ast::Expr) -> Option { } } - normalize(name) + normalize(name, edition) } -fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option { +fn from_param( + expr: &ast::Expr, + sema: &Semantics<'_, RootDatabase>, + edition: Edition, +) -> Option { let arg_list = expr.syntax().parent().and_then(ast::ArgList::cast)?; let args_parent = arg_list.syntax().parent()?; let func = match_ast! { @@ -362,7 +367,7 @@ fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option Option { @@ -374,10 +379,13 @@ fn var_name_from_pat(pat: &ast::Pat) -> Option { } } -fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option { +fn from_type( + expr: &ast::Expr, + sema: &Semantics<'_, RootDatabase>, + edition: Edition, +) -> Option { let ty = sema.type_of_expr(expr)?.adjusted(); let ty = ty.remove_ref().unwrap_or(ty); - let edition = sema.scope(expr.syntax())?.krate().edition(sema.db); name_of_type(&ty, sema.db, edition) } @@ -417,7 +425,7 @@ fn name_of_type<'db>( } else { return None; }; - normalize(&name) + normalize(&name, edition) } fn sequence_name<'db>( @@ -450,13 +458,13 @@ fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Optio Some(name) } -fn from_field_name(expr: &ast::Expr) -> Option { +fn from_field_name(expr: &ast::Expr, edition: syntax::Edition) -> Option { let field = match expr { ast::Expr::FieldExpr(field) => field, _ => return None, }; let ident = field.name_ref()?.ident_token()?; - normalize(ident.text()) + normalize(ident.text(), edition) } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index c0a74380810b..3c3ac9d3bbe6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -332,7 +332,7 @@ pub(crate) fn find_fn_or_blocks( ast::BlockExpr(blk) => { match blk.modifier() { Some(ast::BlockModifier::Async(_)) => blk.syntax().clone(), - Some(ast::BlockModifier::Try(_)) if token_kind != T![return] => blk.syntax().clone(), + Some(ast::BlockModifier::Try { .. }) if token_kind != T![return] => blk.syntax().clone(), _ => continue, } }, @@ -404,8 +404,8 @@ fn nav_for_exit_points( let blk_in_file = InFile::new(file_id, blk.into()); Some(expr_to_nav(db, blk_in_file, Some(async_tok))) }, - Some(ast::BlockModifier::Try(_)) if token_kind != T![return] => { - let try_tok = blk.try_token()?.text_range(); + Some(ast::BlockModifier::Try { .. }) if token_kind != T![return] => { + let try_tok = blk.try_block_modifier()?.try_token()?.text_range(); let blk_in_file = InFile::new(file_id, blk.into()); Some(expr_to_nav(db, blk_in_file, Some(try_tok))) }, diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index fce033382b4b..c8e01e21ec9c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -473,7 +473,7 @@ pub(crate) fn highlight_exit_points( }, ast::BlockExpr(blk) => match blk.modifier() { Some(ast::BlockModifier::Async(t)) => hl_exit_points(sema, Some(t), blk.into()), - Some(ast::BlockModifier::Try(t)) if token.kind() != T![return] => { + Some(ast::BlockModifier::Try { try_token: t, .. }) if token.kind() != T![return] => { hl_exit_points(sema, Some(t), blk.into()) }, _ => continue, diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 15ea92d1c6ec..cf5f137cdd03 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -74,7 +74,7 @@ pub(super) fn try_expr( ast::Fn(fn_) => sema.to_def(&fn_)?.ret_type(sema.db), ast::Item(__) => return None, ast::ClosureExpr(closure) => sema.type_of_expr(&closure.body()?)?.original, - ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try(_)| ast::BlockModifier::Const(_))) { + ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try { .. } | ast::BlockModifier::Const(_))) { sema.type_of_expr(&block_expr.into())?.original } else { continue; diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index c74e3104c14e..caf7cc714d33 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -1382,4 +1382,21 @@ fn f<'a>() { "#]], ); } + + #[test] + fn ref_multi_trait_impl_trait() { + check_with_config( + InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: sized +trait Eq {} +trait Ord {} + +fn foo(argument: &(impl Eq + Ord)) { + let x = argument; + // ^ &(impl Eq + Ord) +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 930eaf2262d9..2e618550f92f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -67,7 +67,7 @@ FxHashMap, FxIndexSet, LineIndexDatabase, base_db::{ CrateOrigin, CrateWorkspaceData, Env, FileSet, RootQueryDb, SourceDatabase, VfsPath, - salsa::{Cancelled, Database}, + salsa::{CancellationToken, Cancelled, Database}, }, prime_caches, symbol_index, }; @@ -947,6 +947,10 @@ fn with_db(&self, f: F) -> Cancellable // We use `attach_db_allow_change()` and not `attach_db()` because fixture injection can change the database. hir::attach_db_allow_change(&self.db, || Cancelled::catch(|| f(&self.db))) } + + pub fn cancellation_token(&self) -> CancellationToken { + self.db.cancellation_token() + } } #[test] diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 9ab07565e9ef..f86974b4ec76 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -1975,8 +1975,8 @@ trait Sub: Super + Super { fn f() -> impl Sub<$0 "#, expect![[r#" - trait Sub - ^^^^^^^^^ ----------- + trait Sub + ^^^^^^^^^^^ --------- "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index e64fd6488f2a..ce1df6a1e7dc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -14,7 +14,7 @@ use std::ops::ControlFlow; use either::Either; -use hir::{DefWithBody, EditionedFileId, InFile, InRealFile, MacroKind, Name, Semantics}; +use hir::{DefWithBody, EditionedFileId, InFile, InRealFile, MacroKind, Semantics}; use ide_db::{FxHashMap, FxHashSet, MiniCore, Ranker, RootDatabase, SymbolKind}; use syntax::{ AstNode, AstToken, NodeOrToken, @@ -257,8 +257,7 @@ fn item(&self) -> &ast::Item { // FIXME: accommodate range highlighting let mut body_stack: Vec> = vec![]; - let mut per_body_cache: FxHashMap, FxHashMap)> = - FxHashMap::default(); + let mut per_body_cache: FxHashMap> = FxHashMap::default(); // Walk all nodes, keeping track of whether we are inside a macro or not. // If in macro, expand it first and highlight the expanded code. @@ -422,14 +421,11 @@ fn item(&self) -> &ast::Item { } let edition = descended_element.file_id.edition(sema.db); - let (unsafe_ops, bindings_shadow_count) = match current_body { - Some(current_body) => { - let (ops, bindings) = per_body_cache - .entry(current_body) - .or_insert_with(|| (sema.get_unsafe_ops(current_body), Default::default())); - (&*ops, Some(bindings)) - } - None => (&empty, None), + let unsafe_ops = match current_body { + Some(current_body) => per_body_cache + .entry(current_body) + .or_insert_with(|| sema.get_unsafe_ops(current_body)), + None => &empty, }; let is_unsafe_node = |node| unsafe_ops.contains(&InFile::new(descended_element.file_id, node)); @@ -438,7 +434,6 @@ fn item(&self) -> &ast::Item { let hl = highlight::name_like( sema, krate, - bindings_shadow_count, &is_unsafe_node, config.syntactic_name_ref_highlighting, name_like, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index dcc9a8c0d5f7..a94bbc9f041d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -5,12 +5,11 @@ use either::Either; use hir::{AsAssocItem, HasAttrs, HasVisibility, Semantics}; use ide_db::{ - FxHashMap, RootDatabase, SymbolKind, + RootDatabase, SymbolKind, defs::{Definition, IdentClass, NameClass, NameRefClass}, syntax_helpers::node_ext::walk_pat, }; use span::Edition; -use stdx::hash_once; use syntax::{ AstNode, AstPtr, AstToken, NodeOrToken, SyntaxKind::{self, *}, @@ -64,7 +63,6 @@ pub(super) fn token( pub(super) fn name_like( sema: &Semantics<'_, RootDatabase>, krate: Option, - bindings_shadow_count: Option<&mut FxHashMap>, is_unsafe_node: &impl Fn(AstPtr>) -> bool, syntactic_name_ref_highlighting: bool, name_like: ast::NameLike, @@ -75,22 +73,15 @@ pub(super) fn name_like( ast::NameLike::NameRef(name_ref) => highlight_name_ref( sema, krate, - bindings_shadow_count, &mut binding_hash, is_unsafe_node, syntactic_name_ref_highlighting, name_ref, edition, ), - ast::NameLike::Name(name) => highlight_name( - sema, - bindings_shadow_count, - &mut binding_hash, - is_unsafe_node, - krate, - name, - edition, - ), + ast::NameLike::Name(name) => { + highlight_name(sema, &mut binding_hash, is_unsafe_node, krate, name, edition) + } ast::NameLike::Lifetime(lifetime) => match IdentClass::classify_lifetime(sema, &lifetime) { Some(IdentClass::NameClass(NameClass::Definition(def))) => { highlight_def(sema, krate, def, edition, false) | HlMod::Definition @@ -273,7 +264,6 @@ fn keyword(token: SyntaxToken, kind: SyntaxKind) -> Highlight { fn highlight_name_ref( sema: &Semantics<'_, RootDatabase>, krate: Option, - bindings_shadow_count: Option<&mut FxHashMap>, binding_hash: &mut Option, is_unsafe_node: &impl Fn(AstPtr>) -> bool, syntactic_name_ref_highlighting: bool, @@ -306,12 +296,8 @@ fn highlight_name_ref( }; let mut h = match name_class { NameRefClass::Definition(def, _) => { - if let Definition::Local(local) = &def - && let Some(bindings_shadow_count) = bindings_shadow_count - { - let name = local.name(sema.db); - let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); - *binding_hash = Some(calc_binding_hash(&name, *shadow_count)) + if let Definition::Local(local) = &def { + *binding_hash = Some(local.as_id() as u64); }; let mut h = highlight_def(sema, krate, def, edition, true); @@ -432,7 +418,6 @@ fn highlight_name_ref( fn highlight_name( sema: &Semantics<'_, RootDatabase>, - bindings_shadow_count: Option<&mut FxHashMap>, binding_hash: &mut Option, is_unsafe_node: &impl Fn(AstPtr>) -> bool, krate: Option, @@ -440,13 +425,8 @@ fn highlight_name( edition: Edition, ) -> Highlight { let name_kind = NameClass::classify(sema, &name); - if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind - && let Some(bindings_shadow_count) = bindings_shadow_count - { - let name = local.name(sema.db); - let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); - *shadow_count += 1; - *binding_hash = Some(calc_binding_hash(&name, *shadow_count)) + if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind { + *binding_hash = Some(local.as_id() as u64); }; match name_kind { Some(NameClass::Definition(def)) => { @@ -474,10 +454,6 @@ fn highlight_name( } } -fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 { - hash_once::((name.as_str(), shadow_count)) -} - pub(super) fn highlight_def( sema: &Semantics<'_, RootDatabase>, krate: Option, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html index d5401e7aec91..7c64707ac1f9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html @@ -42,14 +42,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
fn main() {
-    let hello = "hello";
-    let x = hello.to_string();
-    let y = hello.to_string();
+    let hello = "hello";
+    let x = hello.to_string();
+    let y = hello.to_string();
 
-    let x = "other color please!";
-    let y = x.to_string();
+    let x = "other color please!";
+    let y = x.to_string();
 }
 
 fn bar() {
-    let mut hello = "hello";
+    let mut hello = "hello";
 }
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index 0381865fed45..f8b0dbfe6282 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -17,7 +17,10 @@ use either::Either; use hir::EditionedFileId; -use ide_db::{FilePosition, RootDatabase, base_db::RootQueryDb}; +use ide_db::{ + FilePosition, RootDatabase, + base_db::{RootQueryDb, SourceDatabase}, +}; use span::Edition; use std::iter; @@ -70,11 +73,12 @@ pub(crate) fn on_char_typed( if !TRIGGER_CHARS.contains(&char_typed) { return None; } - // FIXME: We need to figure out the edition of the file here, but that means hitting the - // database for more than just parsing the file which is bad. + let edition = db + .source_root_crates(db.file_source_root(position.file_id).source_root_id(db)) + .first() + .map_or(Edition::CURRENT, |crates| crates.data(db).edition); // FIXME: We are hitting the database here, if we are unlucky this call might block momentarily - // causing the editor to feel sluggish! - let edition = Edition::CURRENT_FIXME; + // causing the editor to feel sluggish! We need to make this bail if it would block too long? let editioned_file_id_wrapper = EditionedFileId::from_span_guess_origin( db, span::EditionedFileId::new(position.file_id, edition), @@ -457,8 +461,8 @@ fn do_type_char(char_typed: char, before: &str) -> Option { let (offset, mut before) = extract_offset(before); let edit = TextEdit::insert(offset, char_typed.to_string()); edit.apply(&mut before); - let parse = SourceFile::parse(&before, span::Edition::CURRENT_FIXME); - on_char_typed_(&parse, offset, char_typed, span::Edition::CURRENT_FIXME).map(|it| { + let parse = SourceFile::parse(&before, span::Edition::CURRENT); + on_char_typed_(&parse, offset, char_typed, span::Edition::CURRENT).map(|it| { it.apply(&mut before); before.to_string() }) diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 2be4e41f4f1a..cc09a1aae7a6 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -110,6 +110,7 @@ pub(super) fn prefill() -> DashMap> { win64_dash_unwind = "win64-unwind", x86_dash_interrupt = "x86-interrupt", rust_dash_preserve_dash_none = "preserve-none", + _0_u8 = "0_u8", @PLAIN: __ra_fixup, @@ -285,6 +286,7 @@ pub(super) fn prefill() -> DashMap> { Into, into_future, into_iter, + into_try_type, IntoFuture, IntoIter, IntoIterator, diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index d83e2eb2b4ae..b75474ee2b86 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -976,11 +976,17 @@ fn break_expr(p: &mut Parser<'_>, r: Restrictions) -> CompletedMarker { // test try_block_expr // fn foo() { // let _ = try {}; +// let _ = try bikeshed T {}; // } fn try_block_expr(p: &mut Parser<'_>, m: Option) -> CompletedMarker { assert!(p.at(T![try])); let m = m.unwrap_or_else(|| p.start()); + let try_modifier = p.start(); p.bump(T![try]); + if p.eat_contextual_kw(T![bikeshed]) { + type_(p); + } + try_modifier.complete(p, TRY_BLOCK_MODIFIER); if p.at(T!['{']) { stmt_list(p); } else { diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 5d22d966b2b7..a2295e449550 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -114,6 +114,7 @@ pub enum SyntaxKind { ATT_SYNTAX_KW, AUTO_KW, AWAIT_KW, + BIKESHED_KW, BUILTIN_KW, CLOBBER_ABI_KW, DEFAULT_KW, @@ -285,6 +286,7 @@ pub enum SyntaxKind { STRUCT, TOKEN_TREE, TRAIT, + TRY_BLOCK_MODIFIER, TRY_EXPR, TUPLE_EXPR, TUPLE_FIELD, @@ -458,6 +460,7 @@ pub const fn text(self) -> &'static str { | STRUCT | TOKEN_TREE | TRAIT + | TRY_BLOCK_MODIFIER | TRY_EXPR | TUPLE_EXPR | TUPLE_FIELD @@ -596,6 +599,7 @@ pub const fn text(self) -> &'static str { ASM_KW => "asm", ATT_SYNTAX_KW => "att_syntax", AUTO_KW => "auto", + BIKESHED_KW => "bikeshed", BUILTIN_KW => "builtin", CLOBBER_ABI_KW => "clobber_abi", DEFAULT_KW => "default", @@ -698,6 +702,7 @@ pub fn is_contextual_keyword(self, edition: Edition) -> bool { ASM_KW => true, ATT_SYNTAX_KW => true, AUTO_KW => true, + BIKESHED_KW => true, BUILTIN_KW => true, CLOBBER_ABI_KW => true, DEFAULT_KW => true, @@ -788,6 +793,7 @@ pub fn is_keyword(self, edition: Edition) -> bool { ASM_KW => true, ATT_SYNTAX_KW => true, AUTO_KW => true, + BIKESHED_KW => true, BUILTIN_KW => true, CLOBBER_ABI_KW => true, DEFAULT_KW => true, @@ -941,6 +947,7 @@ pub fn from_contextual_keyword(ident: &str, edition: Edition) -> Option ASM_KW, "att_syntax" => ATT_SYNTAX_KW, "auto" => AUTO_KW, + "bikeshed" => BIKESHED_KW, "builtin" => BUILTIN_KW, "clobber_abi" => CLOBBER_ABI_KW, "default" => DEFAULT_KW, @@ -1112,6 +1119,7 @@ pub fn from_char(c: char) -> Option { [asm] => { $ crate :: SyntaxKind :: ASM_KW }; [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW }; [auto] => { $ crate :: SyntaxKind :: AUTO_KW }; + [bikeshed] => { $ crate :: SyntaxKind :: BIKESHED_KW }; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW }; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW }; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW }; diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0042_weird_blocks.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0042_weird_blocks.rast index d6d2e75cca67..9e4e9dbf9d25 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0042_weird_blocks.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0042_weird_blocks.rast @@ -45,7 +45,8 @@ SOURCE_FILE WHITESPACE " " EXPR_STMT BLOCK_EXPR - TRY_KW "try" + TRY_BLOCK_MODIFIER + TRY_KW "try" WHITESPACE " " LITERAL INT_NUMBER "92" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_block_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_block_expr.rast index aec8fbf4775c..472ce711c5fe 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_block_expr.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_block_expr.rast @@ -21,7 +21,42 @@ SOURCE_FILE EQ "=" WHITESPACE " " BLOCK_EXPR - TRY_KW "try" + TRY_BLOCK_MODIFIER + TRY_KW "try" + WHITESPACE " " + STMT_LIST + L_CURLY "{" + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BLOCK_EXPR + TRY_BLOCK_MODIFIER + TRY_KW "try" + WHITESPACE " " + BIKESHED_KW "bikeshed" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "T" + GENERIC_ARG_LIST + L_ANGLE "<" + TYPE_ARG + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "U" + R_ANGLE ">" WHITESPACE " " STMT_LIST L_CURLY "{" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_block_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_block_expr.rs index 0f1b41eb64b4..719980473c3b 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_block_expr.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/try_block_expr.rs @@ -1,3 +1,4 @@ fn foo() { let _ = try {}; + let _ = try bikeshed T {}; } diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index f244c9736c7c..546a1e05a063 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -275,7 +275,10 @@ pub fn load_workspace( } tracing::debug!("Stitching sysroot library: {src_root}"); - let mut stitched = stitched::Stitched { crates: Default::default() }; + let mut stitched = stitched::Stitched { + crates: Default::default(), + edition: span::Edition::Edition2024, + }; for path in stitched::SYSROOT_CRATES.trim().lines() { let name = path.split('/').next_back().unwrap(); @@ -511,6 +514,7 @@ pub(crate) mod stitched { #[derive(Debug, Clone, Eq, PartialEq)] pub struct Stitched { pub(super) crates: Arena, + pub(crate) edition: span::Edition, } impl ops::Index for Stitched { diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 8f15f7e1507c..581b5fa51446 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -1831,7 +1831,7 @@ fn sysroot_to_crate_graph( let display_name = CrateDisplayName::from_canonical_name(&stitched[krate].name); let crate_id = crate_graph.add_crate_root( file_id, - Edition::CURRENT_FIXME, + stitched.edition, Some(display_name), None, cfg_options.clone(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 512c231990cb..c74f4550fd89 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -22,7 +22,6 @@ pub(crate) use cargo_metadata::diagnostic::{ Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan, }; -use toolchain::DISPLAY_COMMAND_IGNORE_ENVS; use toolchain::Tool; use triomphe::Arc; @@ -144,6 +143,7 @@ pub(crate) fn invocation_strategy(&self) -> InvocationStrategy { } impl fmt::Display for FlycheckConfig { + /// Show a shortened version of the check command. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { FlycheckConfig::Automatic { cargo_options, .. } => { @@ -153,12 +153,23 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Don't show `my_custom_check --foo $saved_file` literally to the user, as it // looks like we've forgotten to substitute $saved_file. // + // `my_custom_check --foo /home/user/project/src/dir/foo.rs` is too verbose. + // // Instead, show `my_custom_check --foo ...`. The // actual path is often too long to be worth showing // in the IDE (e.g. in the VS Code status bar). let display_args = args .iter() - .map(|arg| if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { "..." } else { arg }) + .map(|arg| { + if (arg == SAVED_FILE_PLACEHOLDER_DOLLAR) + || (arg == SAVED_FILE_INLINE) + || arg.ends_with(".rs") + { + "..." + } else { + arg + } + }) .collect::>(); write!(f, "{command} {}", display_args.join(" ")) @@ -403,24 +414,30 @@ 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. - command_handle: Option>, + command_handle: Option>, /// The receiver side of the channel mentioned above. - command_receiver: Option>, + command_receiver: Option>, diagnostics_cleared_for: FxHashSet, diagnostics_received: DiagnosticsReceived, } -#[derive(PartialEq)] +#[derive(PartialEq, Debug)] enum DiagnosticsReceived { - Yes, - No, - YesAndClearedForAll, + /// We started a flycheck, but we haven't seen any diagnostics yet. + NotYet, + /// We received a non-zero number of diagnostics from rustc or clippy (via + /// cargo or custom check command). This means there were errors or + /// warnings. + AtLeastOne, + /// We received a non-zero number of diagnostics, and the scope is + /// workspace, so we've discarded the previous workspace diagnostics. + AtLeastOneAndClearedWorkspace, } #[allow(clippy::large_enum_variant)] enum Event { RequestStateChange(StateChange), - CheckEvent(Option), + CheckEvent(Option), } /// This is stable behaviour. Don't change. @@ -511,7 +528,7 @@ fn new( command_handle: None, command_receiver: None, diagnostics_cleared_for: Default::default(), - diagnostics_received: DiagnosticsReceived::No, + diagnostics_received: DiagnosticsReceived::NotYet, } } @@ -563,23 +580,13 @@ fn run(mut self, inbox: Receiver) { }; let debug_command = format!("{command:?}"); - let user_facing_command = match origin { - // Don't show all the --format=json-with-blah-blah args, just the simple - // version - FlycheckCommandOrigin::Cargo => self.config.to_string(), - // show them the full command but pretty printed. advanced user - FlycheckCommandOrigin::ProjectJsonRunnable - | FlycheckCommandOrigin::CheckOverrideCommand => display_command( - &command, - Some(std::path::Path::new(self.root.as_path())), - ), - }; + let user_facing_command = self.config.to_string(); tracing::debug!(?origin, ?command, "will restart flycheck"); let (sender, receiver) = unbounded(); match CommandHandle::spawn( command, - CargoCheckParser, + CheckParser, sender, match &self.config { FlycheckConfig::Automatic { cargo_options, .. } => { @@ -640,7 +647,7 @@ fn run(mut self, inbox: Receiver) { error ); } - if self.diagnostics_received == DiagnosticsReceived::No { + if self.diagnostics_received == DiagnosticsReceived::NotYet { tracing::trace!(flycheck_id = self.id, "clearing diagnostics"); // We finished without receiving any diagnostics. // Clear everything for good measure @@ -699,7 +706,7 @@ fn run(mut self, inbox: Receiver) { self.report_progress(Progress::DidFinish(res)); } Event::CheckEvent(Some(message)) => match message { - CargoCheckMessage::CompilerArtifact(msg) => { + CheckMessage::CompilerArtifact(msg) => { tracing::trace!( flycheck_id = self.id, artifact = msg.target.name, @@ -729,46 +736,75 @@ fn run(mut self, inbox: Receiver) { }); } } - CargoCheckMessage::Diagnostic { diagnostic, package_id } => { + CheckMessage::Diagnostic { diagnostic, package_id } => { tracing::trace!( flycheck_id = self.id, message = diagnostic.message, package_id = package_id.as_ref().map(|it| it.as_str()), + scope = ?self.scope, "diagnostic received" ); - if self.diagnostics_received == DiagnosticsReceived::No { - self.diagnostics_received = DiagnosticsReceived::Yes; - } - if let Some(package_id) = &package_id { - if self.diagnostics_cleared_for.insert(package_id.clone()) { - tracing::trace!( - flycheck_id = self.id, - package_id = package_id.as_str(), - "clearing diagnostics" - ); - self.send(FlycheckMessage::ClearDiagnostics { + + match &self.scope { + FlycheckScope::Workspace => { + if self.diagnostics_received == DiagnosticsReceived::NotYet { + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + kind: ClearDiagnosticsKind::All(ClearScope::Workspace), + }); + + self.diagnostics_received = + DiagnosticsReceived::AtLeastOneAndClearedWorkspace; + } + + if let Some(package_id) = package_id { + tracing::warn!( + "Ignoring package label {:?} and applying diagnostics to the whole workspace", + package_id + ); + } + + self.send(FlycheckMessage::AddDiagnostic { id: self.id, - kind: ClearDiagnosticsKind::All(ClearScope::Package( - package_id.clone(), - )), + generation: self.generation, + package_id: None, + workspace_root: self.root.clone(), + diagnostic, + }); + } + FlycheckScope::Package { package: flycheck_package, .. } => { + if self.diagnostics_received == DiagnosticsReceived::NotYet { + self.diagnostics_received = DiagnosticsReceived::AtLeastOne; + } + + // If the package has been set in the diagnostic JSON, respect that. Otherwise, use the + // package that the current flycheck is scoped to. This is useful when a project is + // directly using rustc for its checks (e.g. custom check commands in rust-project.json). + let package_id = package_id.unwrap_or(flycheck_package.clone()); + + if self.diagnostics_cleared_for.insert(package_id.clone()) { + tracing::trace!( + flycheck_id = self.id, + package_id = package_id.as_str(), + "clearing diagnostics" + ); + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + kind: ClearDiagnosticsKind::All(ClearScope::Package( + package_id.clone(), + )), + }); + } + + self.send(FlycheckMessage::AddDiagnostic { + id: self.id, + generation: self.generation, + package_id: Some(package_id), + workspace_root: self.root.clone(), + diagnostic, }); } - } else if self.diagnostics_received - != DiagnosticsReceived::YesAndClearedForAll - { - self.diagnostics_received = DiagnosticsReceived::YesAndClearedForAll; - self.send(FlycheckMessage::ClearDiagnostics { - id: self.id, - kind: ClearDiagnosticsKind::All(ClearScope::Workspace), - }); } - self.send(FlycheckMessage::AddDiagnostic { - id: self.id, - generation: self.generation, - package_id, - workspace_root: self.root.clone(), - diagnostic, - }); } }, } @@ -792,7 +828,7 @@ fn cancel_check_process(&mut self) { fn clear_diagnostics_state(&mut self) { self.diagnostics_cleared_for.clear(); - self.diagnostics_received = DiagnosticsReceived::No; + self.diagnostics_received = DiagnosticsReceived::NotYet; } fn explicit_check_command( @@ -942,15 +978,18 @@ fn send(&self, check_task: FlycheckMessage) { } #[allow(clippy::large_enum_variant)] -enum CargoCheckMessage { +enum CheckMessage { + /// A message from `cargo check`, including details like the path + /// to the relevant `Cargo.toml`. CompilerArtifact(cargo_metadata::Artifact), + /// A diagnostic message from rustc itself. Diagnostic { diagnostic: Diagnostic, package_id: Option }, } -struct CargoCheckParser; +struct CheckParser; -impl JsonLinesParser for CargoCheckParser { - fn from_line(&self, line: &str, error: &mut String) -> Option { +impl JsonLinesParser for CheckParser { + fn from_line(&self, line: &str, error: &mut String) -> Option { let mut deserializer = serde_json::Deserializer::from_str(line); deserializer.disable_recursion_limit(); if let Ok(message) = JsonMessage::deserialize(&mut deserializer) { @@ -958,10 +997,10 @@ fn from_line(&self, line: &str, error: &mut String) -> Option // Skip certain kinds of messages to only spend time on what's useful JsonMessage::Cargo(message) => match message { cargo_metadata::Message::CompilerArtifact(artifact) if !artifact.fresh => { - Some(CargoCheckMessage::CompilerArtifact(artifact)) + Some(CheckMessage::CompilerArtifact(artifact)) } cargo_metadata::Message::CompilerMessage(msg) => { - Some(CargoCheckMessage::Diagnostic { + Some(CheckMessage::Diagnostic { diagnostic: msg.message, package_id: Some(PackageSpecifier::Cargo { package_id: Arc::new(msg.package_id), @@ -971,7 +1010,7 @@ fn from_line(&self, line: &str, error: &mut String) -> Option _ => None, }, JsonMessage::Rustc(message) => { - Some(CargoCheckMessage::Diagnostic { diagnostic: message, package_id: None }) + Some(CheckMessage::Diagnostic { diagnostic: message, package_id: None }) } }; } @@ -981,7 +1020,7 @@ fn from_line(&self, line: &str, error: &mut String) -> Option None } - fn from_eof(&self) -> Option { + fn from_eof(&self) -> Option { None } } @@ -993,64 +1032,14 @@ enum JsonMessage { Rustc(Diagnostic), } -/// Not good enough to execute in a shell, but good enough to show the user without all the noisy -/// quotes -/// -/// Pass implicit_cwd if there is one regarded as the obvious by the user, so we can skip showing it. -/// Compactness is the aim of the game, the output typically gets truncated quite a lot. -fn display_command(c: &Command, implicit_cwd: Option<&std::path::Path>) -> String { - let mut o = String::new(); - use std::fmt::Write; - let lossy = std::ffi::OsStr::to_string_lossy; - if let Some(dir) = c.get_current_dir() { - if Some(dir) == implicit_cwd.map(std::path::Path::new) { - // pass - } else if dir.to_string_lossy().contains(" ") { - write!(o, "cd {:?} && ", dir).unwrap(); - } else { - write!(o, "cd {} && ", dir.display()).unwrap(); - } - } - for (env, val) in c.get_envs() { - let (env, val) = (lossy(env), val.map(lossy).unwrap_or(std::borrow::Cow::Borrowed(""))); - if DISPLAY_COMMAND_IGNORE_ENVS.contains(&env.as_ref()) { - continue; - } - if env.contains(" ") { - write!(o, "\"{}={}\" ", env, val).unwrap(); - } else if val.contains(" ") { - write!(o, "{}=\"{}\" ", env, val).unwrap(); - } else { - write!(o, "{}={} ", env, val).unwrap(); - } - } - let prog = lossy(c.get_program()); - if prog.contains(" ") { - write!(o, "{:?}", prog).unwrap(); - } else { - write!(o, "{}", prog).unwrap(); - } - for arg in c.get_args() { - let arg = lossy(arg); - if arg.contains(" ") { - write!(o, " \"{}\"", arg).unwrap(); - } else { - write!(o, " {}", arg).unwrap(); - } - } - o -} - #[cfg(test)] mod tests { + use super::*; use ide_db::FxHashMap; use itertools::Itertools; use paths::Utf8Path; use project_model::project_json; - use crate::flycheck::Substitutions; - use crate::flycheck::display_command; - #[test] fn test_substitutions() { let label = ":label"; @@ -1139,34 +1128,47 @@ fn test_substitute( } #[test] - fn test_display_command() { - use std::path::Path; - let workdir = Path::new("workdir"); - let mut cmd = toolchain::command("command", workdir, &FxHashMap::default()); - assert_eq!(display_command(cmd.arg("--arg"), Some(workdir)), "command --arg"); - assert_eq!( - display_command(cmd.arg("spaced arg"), Some(workdir)), - "command --arg \"spaced arg\"" - ); - assert_eq!( - display_command(cmd.env("ENVIRON", "yeah"), Some(workdir)), - "ENVIRON=yeah command --arg \"spaced arg\"" - ); - assert_eq!( - display_command(cmd.env("OTHER", "spaced env"), Some(workdir)), - "ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" - ); - assert_eq!( - display_command(cmd.current_dir("/tmp"), Some(workdir)), - "cd /tmp && ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" - ); - assert_eq!( - display_command(cmd.current_dir("/tmp and/thing"), Some(workdir)), - "cd \"/tmp and/thing\" && ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" - ); - assert_eq!( - display_command(cmd.current_dir("/tmp and/thing"), Some(Path::new("/tmp and/thing"))), - "ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" - ); + fn test_flycheck_config_display() { + let clippy = FlycheckConfig::Automatic { + cargo_options: CargoOptions { + subcommand: "clippy".to_owned(), + target_tuples: vec![], + all_targets: false, + set_test: false, + no_default_features: false, + all_features: false, + features: vec![], + extra_args: vec![], + extra_test_bin_args: vec![], + extra_env: FxHashMap::default(), + target_dir_config: TargetDirectoryConfig::default(), + }, + ansi_color_output: true, + }; + assert_eq!(clippy.to_string(), "cargo clippy"); + + let custom_dollar = FlycheckConfig::CustomCommand { + command: "check".to_owned(), + args: vec!["--input".to_owned(), "$saved_file".to_owned()], + extra_env: FxHashMap::default(), + invocation_strategy: InvocationStrategy::Once, + }; + assert_eq!(custom_dollar.to_string(), "check --input ..."); + + let custom_inline = FlycheckConfig::CustomCommand { + command: "check".to_owned(), + args: vec!["--input".to_owned(), "{saved_file}".to_owned()], + extra_env: FxHashMap::default(), + invocation_strategy: InvocationStrategy::Once, + }; + assert_eq!(custom_inline.to_string(), "check --input ..."); + + let custom_rs = FlycheckConfig::CustomCommand { + command: "check".to_owned(), + args: vec!["--input".to_owned(), "/path/to/file.rs".to_owned()], + extra_env: FxHashMap::default(), + invocation_strategy: InvocationStrategy::Once, + }; + assert_eq!(custom_rs.to_string(), "check --input ..."); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index afd4162de622..1462727df4e1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -14,7 +14,7 @@ use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId}; use ide_db::{ MiniCore, - base_db::{Crate, ProcMacroPaths, SourceDatabase, salsa::Revision}, + base_db::{Crate, ProcMacroPaths, SourceDatabase, salsa::CancellationToken, salsa::Revision}, }; use itertools::Itertools; use load_cargo::SourceRootConfig; @@ -88,6 +88,7 @@ pub(crate) struct GlobalState { pub(crate) task_pool: Handle, Receiver>, pub(crate) fmt_pool: Handle, Receiver>, pub(crate) cancellation_pool: thread::Pool, + pub(crate) cancellation_tokens: FxHashMap, pub(crate) config: Arc, pub(crate) config_errors: Option, @@ -265,6 +266,7 @@ pub(crate) fn new(sender: Sender, config: Config) -> Global task_pool, fmt_pool, cancellation_pool, + cancellation_tokens: Default::default(), loader, config: Arc::new(config.clone()), analysis_host, @@ -617,6 +619,7 @@ pub(crate) fn register_request( } pub(crate) fn respond(&mut self, response: lsp_server::Response) { + self.cancellation_tokens.remove(&response.id); if let Some((method, start)) = self.req_queue.incoming.complete(&response.id) { if let Some(err) = &response.error && err.message.starts_with("server panicked") @@ -631,6 +634,9 @@ pub(crate) fn respond(&mut self, response: lsp_server::Response) { } pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) { + if let Some(token) = self.cancellation_tokens.remove(&request_id) { + token.cancel(); + } if let Some(response) = self.req_queue.incoming.cancel(request_id) { self.send(response.into()); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index 90deae2d902e..63b4e6430c2d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -253,6 +253,9 @@ fn on_with_thread_intent( tracing::debug!(?params); let world = self.global_state.snapshot(); + self.global_state + .cancellation_tokens + .insert(req.id.clone(), world.analysis.cancellation_token()); if RUSTFMT { &mut self.global_state.fmt_pool.handle } else { @@ -265,7 +268,19 @@ fn on_with_thread_intent( }); match thread_result_to_response::(req.id.clone(), result) { Ok(response) => Task::Response(response), - Err(_cancelled) if ALLOW_RETRYING => Task::Retry(req), + Err(HandlerCancelledError::Inner( + Cancelled::PendingWrite | Cancelled::PropagatedPanic, + )) if ALLOW_RETRYING => Task::Retry(req), + // Note: Technically the return value here does not matter as we have already responded to the client with this error. + Err(HandlerCancelledError::Inner(Cancelled::Local)) => Task::Response(Response { + id: req.id, + result: None, + error: Some(ResponseError { + code: lsp_server::ErrorCode::RequestCanceled as i32, + message: "canceled by client".to_owned(), + data: None, + }), + }), Err(_cancelled) => { let error = on_cancelled(); Task::Response(Response { id: req.id, result: None, error: Some(error) }) diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index fe05ef946518..0a81cef52ec5 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -81,25 +81,24 @@ impl zalsa_::HasJar for SyntaxContext { #[derive(Hash)] struct StructKey<'db, T0, T1, T2, T3>(T0, T1, T2, T3, std::marker::PhantomData<&'db ()>); - impl<'db, T0, T1, T2, T3> zalsa_::interned::HashEqLike> - for SyntaxContextData + impl<'db, T0, T1, T2, T3> zalsa_::HashEqLike> for SyntaxContextData where - Option: zalsa_::interned::HashEqLike, - Transparency: zalsa_::interned::HashEqLike, - Edition: zalsa_::interned::HashEqLike, - SyntaxContext: zalsa_::interned::HashEqLike, + Option: zalsa_::HashEqLike, + Transparency: zalsa_::HashEqLike, + Edition: zalsa_::HashEqLike, + SyntaxContext: zalsa_::HashEqLike, { fn hash(&self, h: &mut H) { - zalsa_::interned::HashEqLike::::hash(&self.outer_expn, &mut *h); - zalsa_::interned::HashEqLike::::hash(&self.outer_transparency, &mut *h); - zalsa_::interned::HashEqLike::::hash(&self.edition, &mut *h); - zalsa_::interned::HashEqLike::::hash(&self.parent, &mut *h); + zalsa_::HashEqLike::::hash(&self.outer_expn, &mut *h); + zalsa_::HashEqLike::::hash(&self.outer_transparency, &mut *h); + zalsa_::HashEqLike::::hash(&self.edition, &mut *h); + zalsa_::HashEqLike::::hash(&self.parent, &mut *h); } fn eq(&self, data: &StructKey<'db, T0, T1, T2, T3>) -> bool { - zalsa_::interned::HashEqLike::::eq(&self.outer_expn, &data.0) - && zalsa_::interned::HashEqLike::::eq(&self.outer_transparency, &data.1) - && zalsa_::interned::HashEqLike::::eq(&self.edition, &data.2) - && zalsa_::interned::HashEqLike::::eq(&self.parent, &data.3) + zalsa_::HashEqLike::::eq(&self.outer_expn, &data.0) + && zalsa_::HashEqLike::::eq(&self.outer_transparency, &data.1) + && zalsa_::HashEqLike::::eq(&self.edition, &data.2) + && zalsa_::HashEqLike::::eq(&self.parent, &data.3) } } impl zalsa_struct_::Configuration for SyntaxContext { @@ -203,10 +202,10 @@ unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool { impl<'db> SyntaxContext { pub fn new< Db, - T0: zalsa_::interned::Lookup> + std::hash::Hash, - T1: zalsa_::interned::Lookup + std::hash::Hash, - T2: zalsa_::interned::Lookup + std::hash::Hash, - T3: zalsa_::interned::Lookup + std::hash::Hash, + T0: zalsa_::Lookup> + std::hash::Hash, + T1: zalsa_::Lookup + std::hash::Hash, + T2: zalsa_::Lookup + std::hash::Hash, + T3: zalsa_::Lookup + std::hash::Hash, >( db: &'db Db, outer_expn: T0, @@ -218,10 +217,10 @@ pub fn new< ) -> Self where Db: ?Sized + salsa::Database, - Option: zalsa_::interned::HashEqLike, - Transparency: zalsa_::interned::HashEqLike, - Edition: zalsa_::interned::HashEqLike, - SyntaxContext: zalsa_::interned::HashEqLike, + Option: zalsa_::HashEqLike, + Transparency: zalsa_::HashEqLike, + Edition: zalsa_::HashEqLike, + SyntaxContext: zalsa_::HashEqLike, { let (zalsa, zalsa_local) = db.zalsas(); @@ -236,10 +235,10 @@ pub fn new< std::marker::PhantomData, ), |id, data| SyntaxContextData { - outer_expn: zalsa_::interned::Lookup::into_owned(data.0), - outer_transparency: zalsa_::interned::Lookup::into_owned(data.1), - edition: zalsa_::interned::Lookup::into_owned(data.2), - parent: zalsa_::interned::Lookup::into_owned(data.3), + outer_expn: zalsa_::Lookup::into_owned(data.0), + outer_transparency: zalsa_::Lookup::into_owned(data.1), + edition: zalsa_::Lookup::into_owned(data.2), + parent: zalsa_::Lookup::into_owned(data.3), opaque: opaque(zalsa_::FromId::from_id(id)), opaque_and_semiopaque: opaque_and_semiopaque(zalsa_::FromId::from_id(id)), }, diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 991fe7d83a0e..544053408f73 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -472,8 +472,11 @@ RefExpr = TryExpr = Attr* Expr '?' +TryBlockModifier = + 'try' ('bikeshed' Type)? + BlockExpr = - Attr* Label? ('try' | 'unsafe' | ('async' 'move'?) | ('gen' 'move'?) | 'const') StmtList + Attr* Label? (TryBlockModifier | 'unsafe' | ('async' 'move'?) | ('gen' 'move'?) | 'const') StmtList PrefixExpr = Attr* op:('-' | '!' | '*') Expr diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs index db6699538138..b44150f86842 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs @@ -375,7 +375,11 @@ pub fn kind(&self) -> LiteralKind { pub enum BlockModifier { Async(SyntaxToken), Unsafe(SyntaxToken), - Try(SyntaxToken), + Try { + try_token: SyntaxToken, + bikeshed_token: Option, + result_type: Option, + }, Const(SyntaxToken), AsyncGen(SyntaxToken), Gen(SyntaxToken), @@ -394,7 +398,13 @@ pub fn modifier(&self) -> Option { }) .or_else(|| self.async_token().map(BlockModifier::Async)) .or_else(|| self.unsafe_token().map(BlockModifier::Unsafe)) - .or_else(|| self.try_token().map(BlockModifier::Try)) + .or_else(|| { + let modifier = self.try_block_modifier()?; + let try_token = modifier.try_token()?; + let bikeshed_token = modifier.bikeshed_token(); + let result_type = modifier.ty(); + Some(BlockModifier::Try { try_token, bikeshed_token, result_type }) + }) .or_else(|| self.const_token().map(BlockModifier::Const)) .or_else(|| self.label().map(BlockModifier::Label)) } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 7b9f5b9166bb..c4e72eafa793 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -323,6 +323,8 @@ pub fn label(&self) -> Option