diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 557dfe09853ba..bdfe7bfb8f1fb 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 ddb5ae33bc98e..80dda5efd1c5f 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -282,6 +282,7 @@ attribute_parsers!( 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 4fb739403cede..853ecd3c91f79 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 361e98454e836..38b73a07a6891 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 d9c4179cca839..bfbf1272e5fca 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 d55cb1d6f66a4..ae9f9899ffd94 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 @@ impl AttributeKind { 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 a1c3af5f999d4..85e96e200c2e1 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_data_structures::fx::{FxIndexMap, FxIndexSet}; 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 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { 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 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { 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 42ef66847858a..e474f106433df 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 fa9995898ac20..a06f927928205 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 3f89ff577d596..f3c98f5d910ed 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -330,6 +330,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcMir(_) | AttributeKind::RustcNeverReturnsNullPointer | AttributeKind::RustcNoImplicitAutorefs + | AttributeKind::RustcNoImplicitBounds | AttributeKind::RustcNonConstTraitMethod | AttributeKind::RustcNounwind | AttributeKind::RustcObjcClass { .. } @@ -413,7 +414,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // 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 ba442d8a0081d..88604c91d0259 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 @@ where } } -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 @@ where #[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 fb735b54dd82c..4b6f5b655760b 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 584c8e2a27c82..bd48068eb579f 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -12,7 +12,7 @@ use rustc_span::DUMMY_SP; 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 661ea4ab6a27f..a9e7c49515c7f 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 320eb97f83a43..5941477201933 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1532,6 +1532,56 @@ impl MaybeUninit<[T; N]> { } } +#[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 a289b0d6df401..ac096afb38af0 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 @@ impl<'a, T: 'a, const N: usize> ArrayWindows<'a, T, N> { } } +// 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 @@ impl<'a, T, const N: usize> Iterator for ArrayWindows<'a, T, N> { 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 @@ impl ExactSizeIterator for ArrayWindows<'_, T, N> { } } +#[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 8f4a79b389f62..02a408802b6fa 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -121,7 +121,7 @@ macro_rules! define_client_side { } } } -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 e5133907854b2..88c4dd6630b15 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 244ab7d81b022..6a9027046af00 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -18,71 +18,67 @@ use crate::{Delimiter, Level}; /// 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 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 literal_from_str(s: &str) -> Result, ()>; + fn emit_diagnostic(diagnostic: Diagnostic<$Span>); + + 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>; - - 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 symbol_normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>; + stream: $TokenStream + ) -> Vec>; + + 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<$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 @@ macro_rules! declare_tags { 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 @@ impl Mark for Marked { 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 @@ mark_noop! { 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 @@ compound_traits!( 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 63329c8c02601..7fee8654bc788 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -52,45 +52,37 @@ macro_rules! rpc_encode_decode { } }; (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),* } - - match self { - $($name::$variant $(($field))* => { - (Tag::$variant as u8).encode(w, s); - $($field.encode(w, s);)* - })* + #[allow(non_upper_case_globals, non_camel_case_types)] + const _: () = { + #[repr(u8)] enum Tag { $($variant),* } + + $(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 a53550e0b9e0c..1a9951af8c9f1 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 073ddb554994c..a3c6a232264e0 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 @@ macro_rules! define_server_dispatcher_impl { $(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 @@ macro_rules! define_server_dispatcher_impl { } } } -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 @@ impl client::Client { ) -> Result where S: Server, - S::TokenStream: Default, { let client::Client { handle_counters, run, _marker } = *self; run_server( @@ -338,7 +335,6 @@ impl client::Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream ) -> 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 49b6f2ae41f89..e2f39c015bdd7 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 25bd7005b9942..14b41a427f1e0 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 c71a350ffd305..54c0f2ec74309 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -949,7 +949,9 @@ impl TargetCfgs { // 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 9813ac7ff500d..230578d79ffbe 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -249,6 +249,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "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 1804490a5f12c..742790a50fce8 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1670,6 +1670,11 @@ impl<'test> TestCx<'test> { 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 2cf3e37a43bce..755ae55eea462 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 2288933a96ccb..04559f15eda41 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 f593f2b2955ac..8136daa8329fd 100644 --- a/src/tools/rust-analyzer/bench_data/glorious_old_parser +++ b/src/tools/rust-analyzer/bench_data/glorious_old_parser @@ -724,7 +724,7 @@ impl<'a> Parser<'a> { // {foo(bar {}} // - ^ // | | - // | help: `)` may belong here (FIXME: #58270) + // | help: `)` may belong here // | // unclosed delimiter if let Some(sp) = unmatched.unclosed_span { @@ -3217,7 +3217,6 @@ impl<'a> Parser<'a> { } _ => { - // FIXME Could factor this out into non_fatal_unexpected or something. let actual = self.this_token_to_string(); self.span_err(self.span, &format!("unexpected token: `{}`", actual)); } @@ -5250,7 +5249,6 @@ impl<'a> Parser<'a> { } } } else { - // FIXME: Bad copy of attrs let old_directory_ownership = mem::replace(&mut self.directory.ownership, DirectoryOwnership::UnownedViaBlock); let item = self.parse_item_(attrs.clone(), false, true)?; @@ -5953,23 +5951,14 @@ impl<'a> Parser<'a> { }); assoc_ty_bindings.push(span); } else if self.check_const_arg() { - // FIXME(const_generics): to distinguish between idents for types and consts, - // we should introduce a GenericArg::Ident in the AST and distinguish when - // lowering to the HIR. For now, idents for const args are not permitted. - // Parse const argument. let expr = if let token::OpenDelim(token::Brace) = self.token { self.parse_block_expr(None, self.span, BlockCheckMode::Default, ThinVec::new())? } else if self.token.is_ident() { - // FIXME(const_generics): to distinguish between idents for types and consts, - // we should introduce a GenericArg::Ident in the AST and distinguish when - // lowering to the HIR. For now, idents for const args are not permitted. return Err( self.fatal("identifiers may currently not be used for const generics") ); } else { - // FIXME(const_generics): this currently conflicts with emplacement syntax - // with negative integer literals. self.parse_literal_maybe_minus()? }; let value = AnonConst { @@ -5991,9 +5980,6 @@ impl<'a> Parser<'a> { } } - // FIXME: we would like to report this in ast_validation instead, but we currently do not - // preserve ordering of generic parameters with respect to associated type binding, so we - // lose that information after parsing. if misplaced_assoc_ty_bindings.len() > 0 { let mut err = self.struct_span_err( args_lo.to(self.prev_span), @@ -6079,8 +6065,6 @@ impl<'a> Parser<'a> { bounds, } )); - // FIXME: Decide what should be used here, `=` or `==`. - // FIXME: We are just dropping the binders in lifetime_defs on the floor here. } else if self.eat(&token::Eq) || self.eat(&token::EqEq) { let rhs_ty = self.parse_ty()?; where_clause.predicates.push(ast::WherePredicate::EqPredicate( 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 13fb05d565479..dd419f48fc7ec 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 @@ const _: () = { } } - 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 f1a1fe596493b..eb4cec39dce06 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 4fbf6d951779e..701586c258386 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 @@ impl<'db> ExprCollector<'db> { 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 @@ impl<'db> ExprCollector<'db> { 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 @@ impl<'db> ExprCollector<'db> { .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 @@ impl<'db> ExprCollector<'db> { 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 @@ impl<'db> ExprCollector<'db> { /// 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 @@ impl<'db> ExprCollector<'db> { 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 @@ impl<'db> ExprCollector<'db> { /// 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 @@ impl<'db> ExprCollector<'db> { 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 @@ impl<'db> ExprCollector<'db> { 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 9e1efb9777869..1303773b59d43 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 @@ impl ItemScope { 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 51dd55301f442..fef92c89b145a 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 @@ language_item_table! { LangItems => 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 323060f61d155..f51524c1b5511 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 @@ impl<'db> DefCollector<'db> { // `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 6e4b96b050887..3029bca1d51fe 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 f3e67d01e5662..0c3c51366861b 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 43b428c3fa51a..4e77e8be364bf 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 either::Either; 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 @@ pub trait HirDisplay<'db> { 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 @@ impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> { 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 @@ impl<'db, T: HirDisplay<'db> + Internable> HirDisplay<'db> for Interned { } } -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 @@ impl<'db> HirDisplay<'db> for Ty<'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 @@ impl<'db> HirDisplay<'db> for Ty<'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(' ')?; - } - match m { - rustc_ast_ir::Mutability::Not => (), - rustc_ast_ir::Mutability::Mut => f.write_str("mut ")?, - } - } else { - write!( - f, - "*{}", - match m { - rustc_ast_ir::Mutability::Not => "const ", - rustc_ast_ir::Mutability::Mut => "mut ", - } - )?; + 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 ")?, } - // 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) - } + 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 => "const ", + rustc_ast_ir::Mutability::Mut => "mut ", } - _ => (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 @@ impl<'db> HirDisplay<'db> for Ty<'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 @@ impl<'db> HirDisplay<'db> for Ty<'db> { Either::Left(*self), &bounds, SizedByDefault::Sized { anchor: krate }, + trait_bounds_need_parens, )?; } TyKind::Closure(id, substs) => { @@ -1525,6 +1468,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { Either::Left(*self), &bounds, SizedByDefault::Sized { anchor: krate }, + trait_bounds_need_parens, )?; } }, @@ -1567,6 +1511,7 @@ impl<'db> HirDisplay<'db> for Ty<'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 5789bf02a42e7..386556b156843 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 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { 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 @@ impl<'db> GenericPredicates { } } +/// 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 36630ab587cd3..438699b40983e 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 dc3869930dcf1..37da7fc875631 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 4f1480c393667..3b5b4e4fa5404 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 @@ where "#, ); } + +#[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 98503452d348b..7d4f04268af9f 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 390553c0d7a95..cdf7b40003b5b 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 7dd73f1e7aa01..148300deb875f 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 @@ use crate::{ TargetFeatures, db::HirDatabase, layout::{Layout, TagEncoding}, + lower::all_supertraits_trait_refs, mir::pad16, }; @@ -62,23 +63,13 @@ pub fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[T /// 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 1f9af564c3599..b4440dfa1826c 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 @@ impl<'db> HirDisplay<'db> for TypeParam { Either::Left(ty), &predicates, SizedByDefault::Sized { anchor: krate }, + false, ); } }, @@ -614,6 +615,7 @@ impl<'db> HirDisplay<'db> for TypeParam { 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 252d71fb80a46..4b615665167c0 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 @@ impl Local { 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 05a89e76529b5..8622aa1378b3b 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 hir_ty::{ 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 cc2bf8174941f..2694910aa6098 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 ca142332d97e7..5a223e11301cd 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 f2363c6f7ba27..124ef509fb895 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 @@ impl FunctionBody { 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 bd66c02b4150a..ded3b0f5acb20 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 fn_arg_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 e42d0ed1b00b0..73e93a3fbf526 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 84f02bdfdba67..fdc5a0fbda359 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 4b8c193057934..5e08cba8e293c 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 @@ use std::slice; 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 @@ impl<'db> ReferenceConversion<'db> { .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 @@ impl<'db> ReferenceConversion<'db> { .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 @@ impl<'db> ReferenceConversion<'db> { .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 7f67ef848ecec..cffc44f8afb33 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 4444ef5d81d54..db1d599d550d7 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 94ecf6a02ddc0..e30b21c139fad 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 b8b9a7a768168..5d1e876ea2984 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 @@ impl NameGenerator { 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 @@ impl NameGenerator { } } -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 c0a74380810b7..3c3ac9d3bbe61 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 fce033382b4bf..c8e01e21ec9ce 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 15ea92d1c6ec5..cf5f137cdd03e 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 c74e3104c14e0..caf7cc714d33d 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 930eaf2262d93..2e618550f92fb 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 @@ use ide_db::{ 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 @@ impl Analysis { // 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 9ab07565e9efc..f86974b4ec76c 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 e64fd6488f2a6..ce1df6a1e7dcd 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 @@ mod tests; 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 traverse( // 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 traverse( } 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 traverse( 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 dcc9a8c0d5f70..a94bbc9f041d3 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 std::ops::ControlFlow; 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 d5401e7aec918..7c64707ac1f9a 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 @@ .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 0381865fed457..f8b0dbfe62824 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 @@ mod on_enter; 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 @@ mod tests { 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 2be4e41f4f1ac..cc09a1aae7a6d 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 @@ define_symbols! { 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 @@ define_symbols! { 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 d83e2eb2b4ae4..b75474ee2b86c 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 5d22d966b2b79..a2295e4495503 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 @@ impl SyntaxKind { | STRUCT | TOKEN_TREE | TRAIT + | TRY_BLOCK_MODIFIER | TRY_EXPR | TUPLE_EXPR | TUPLE_FIELD @@ -596,6 +599,7 @@ impl SyntaxKind { 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 @@ impl SyntaxKind { 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 @@ impl SyntaxKind { 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 @@ impl SyntaxKind { "asm" => 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 @@ macro_rules ! T_ { [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 d6d2e75cca675..9e4e9dbf9d252 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 aec8fbf4775c6..472ce711c5fe6 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 0f1b41eb64b47..719980473c3bf 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 f244c9736c7cc..546a1e05a0635 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 @@ impl Sysroot { } 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 8f15f7e1507c6..581b5fa514461 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 512c231990cb6..c74f4550fd890 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 @@ use serde_derive::Deserialize; 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 @@ impl FlycheckConfig { } 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 @@ impl fmt::Display for FlycheckConfig { // 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 @@ impl FlycheckActor { command_handle: None, command_receiver: None, diagnostics_cleared_for: Default::default(), - diagnostics_received: DiagnosticsReceived::No, + diagnostics_received: DiagnosticsReceived::NotYet, } } @@ -563,23 +580,13 @@ impl FlycheckActor { }; 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 @@ impl FlycheckActor { 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 @@ impl FlycheckActor { 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 @@ impl FlycheckActor { }); } } - 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 @@ impl FlycheckActor { 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 @@ impl FlycheckActor { } #[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 @@ impl JsonLinesParser for CargoCheckParser { // 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 @@ impl JsonLinesParser for CargoCheckParser { _ => None, }, JsonMessage::Rustc(message) => { - Some(CargoCheckMessage::Diagnostic { diagnostic: message, package_id: None }) + Some(CheckMessage::Diagnostic { diagnostic: message, package_id: None }) } }; } @@ -981,7 +1020,7 @@ impl JsonLinesParser for CargoCheckParser { 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 @@ mod tests { } #[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 afd4162de6227..1462727df4e18 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 hir::ChangeWithProcMacros; 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 @@ impl GlobalState { task_pool, fmt_pool, cancellation_pool, + cancellation_tokens: Default::default(), loader, config: Arc::new(config.clone()), analysis_host, @@ -617,6 +619,7 @@ impl GlobalState { } 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 @@ impl GlobalState { } 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 90deae2d902e5..63b4e6430c2d9 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 @@ impl RequestDispatcher<'_> { 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 @@ impl RequestDispatcher<'_> { }); 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 fe05ef9465181..0a81cef52ec5a 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 @@ const _: () = { #[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 @@ const _: () = { 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 @@ const _: () = { ) -> 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 @@ const _: () = { 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 991fe7d83a0e2..544053408f734 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 db66995381388..b44150f86842c 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 @@ impl ast::Literal { 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 @@ impl ast::BlockExpr { }) .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 7b9f5b9166bbb..c4e72eafa7936 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 @@ impl BlockExpr { #[inline] pub fn stmt_list(&self) -> Option { support::child(&self.syntax) } #[inline] + pub fn try_block_modifier(&self) -> Option { support::child(&self.syntax) } + #[inline] pub fn async_token(&self) -> Option { support::token(&self.syntax, T![async]) } #[inline] pub fn const_token(&self) -> Option { support::token(&self.syntax, T![const]) } @@ -331,8 +333,6 @@ impl BlockExpr { #[inline] pub fn move_token(&self) -> Option { support::token(&self.syntax, T![move]) } #[inline] - pub fn try_token(&self) -> Option { support::token(&self.syntax, T![try]) } - #[inline] pub fn unsafe_token(&self) -> Option { support::token(&self.syntax, T![unsafe]) } } pub struct BoxPat { @@ -1630,6 +1630,19 @@ impl Trait { #[inline] pub fn unsafe_token(&self) -> Option { support::token(&self.syntax, T![unsafe]) } } +pub struct TryBlockModifier { + pub(crate) syntax: SyntaxNode, +} +impl TryBlockModifier { + #[inline] + pub fn ty(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn bikeshed_token(&self) -> Option { + support::token(&self.syntax, T![bikeshed]) + } + #[inline] + pub fn try_token(&self) -> Option { support::token(&self.syntax, T![try]) } +} pub struct TryExpr { pub(crate) syntax: SyntaxNode, } @@ -6320,6 +6333,38 @@ impl fmt::Debug for Trait { f.debug_struct("Trait").field("syntax", &self.syntax).finish() } } +impl AstNode for TryBlockModifier { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TRY_BLOCK_MODIFIER + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == TRY_BLOCK_MODIFIER } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for TryBlockModifier { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for TryBlockModifier {} +impl PartialEq for TryBlockModifier { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for TryBlockModifier { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for TryBlockModifier { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TryBlockModifier").field("syntax", &self.syntax).finish() + } +} impl AstNode for TryExpr { #[inline] fn kind() -> SyntaxKind @@ -9979,6 +10024,11 @@ impl std::fmt::Display for Trait { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for TryBlockModifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for TryExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_error.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_error.rs index 1c902893abc64..6f00ef4ed584f 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_error.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_error.rs @@ -9,16 +9,6 @@ use crate::{TextRange, TextSize}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct SyntaxError(String, TextRange); -// FIXME: there was an unused SyntaxErrorKind previously (before this enum was removed) -// It was introduced in this PR: https://github.com/rust-lang/rust-analyzer/pull/846/files#diff-827da9b03b8f9faa1bade5cdd44d5dafR95 -// but it was not removed by a mistake. -// -// So, we need to find a place where to stick validation for attributes in match clauses. -// Code before refactor: -// InvalidMatchInnerAttr => { -// write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression") -// } - impl SyntaxError { pub fn new(message: impl Into, range: TextRange) -> Self { Self(message.into(), range) diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 48c3e89525075..7d95043867ee6 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -43,6 +43,7 @@ //! dispatch_from_dyn: unsize, pin //! hash: sized //! include: +//! include_bytes: //! index: sized //! infallible: //! int_impl: size_of, transmute @@ -953,6 +954,9 @@ pub mod ops { #[lang = "from_residual"] fn from_residual(residual: R) -> Self; } + pub const trait Residual: Sized { + type TryType: [const] Try; + } #[lang = "Try"] pub trait Try: FromResidual { type Output; @@ -962,6 +966,12 @@ pub mod ops { #[lang = "branch"] fn branch(self) -> ControlFlow; } + #[lang = "into_try_type"] + pub const fn residual_into_try_type, O>( + r: R, + ) -> >::TryType { + FromResidual::from_residual(r) + } impl Try for ControlFlow { type Output = C; @@ -985,6 +995,10 @@ pub mod ops { } } } + + impl Residual for ControlFlow { + type TryType = ControlFlow; + } // region:option impl Try for Option { type Output = T; @@ -1008,6 +1022,10 @@ pub mod ops { } } } + + impl const Residual for Option { + type TryType = Option; + } // endregion:option // region:result // region:from @@ -1037,10 +1055,14 @@ pub mod ops { } } } + + impl const Residual for Result { + type TryType = Result; + } // endregion:from // endregion:result } - pub use self::try_::{ControlFlow, FromResidual, Try}; + pub use self::try_::{ControlFlow, FromResidual, Residual, Try}; // endregion:try // region:add @@ -2040,6 +2062,14 @@ mod macros { } // endregion:include + // region:include_bytes + #[rustc_builtin_macro] + #[macro_export] + macro_rules! include_bytes { + ($file:expr $(,)?) => {{ /* compiler built-in */ }}; + } + // endregion:include_bytes + // region:concat #[rustc_builtin_macro] #[macro_export] diff --git a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs index 1a17269838708..39319886cfe4a 100644 --- a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs +++ b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs @@ -74,9 +74,6 @@ impl Tool { // Prevent rustup from automatically installing toolchains, see https://github.com/rust-lang/rust-analyzer/issues/20719. pub const NO_RUSTUP_AUTO_INSTALL_ENV: (&str, &str) = ("RUSTUP_AUTO_INSTALL", "0"); -// These get ignored when displaying what command is running in LSP status messages. -pub const DISPLAY_COMMAND_IGNORE_ENVS: &[&str] = &[NO_RUSTUP_AUTO_INSTALL_ENV.0]; - #[allow(clippy::disallowed_types)] /* generic parameter allows for FxHashMap */ pub fn command( cmd: impl AsRef, diff --git a/src/tools/rust-analyzer/docs/book/README.md b/src/tools/rust-analyzer/docs/book/README.md index 0a3161f3af38d..cd4d8783a4d20 100644 --- a/src/tools/rust-analyzer/docs/book/README.md +++ b/src/tools/rust-analyzer/docs/book/README.md @@ -6,7 +6,7 @@ The rust analyzer manual uses [mdbook](https://rust-lang.github.io/mdBook/). To run the documentation site locally: -```shell +```bash cargo install mdbook cargo xtask codegen cd docs/book diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/README.md b/src/tools/rust-analyzer/docs/book/src/contributing/README.md index c95a1dba6249d..bb2b6081ad956 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/README.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/README.md @@ -4,7 +4,7 @@ rust-analyzer is an ordinary Rust project, which is organized as a Cargo workspa So, just ```bash -$ cargo test +cargo test ``` should be enough to get you started! @@ -203,14 +203,14 @@ It is enabled by `RA_COUNT=1`. To measure time for from-scratch analysis, use something like this: ```bash -$ cargo run --release -p rust-analyzer -- analysis-stats ../chalk/ +cargo run --release -p rust-analyzer -- analysis-stats ../chalk/ ``` For measuring time of incremental analysis, use either of these: ```bash -$ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --highlight ../chalk/chalk-engine/src/logic.rs -$ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --complete ../chalk/chalk-engine/src/logic.rs:94:0 +cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --highlight ../chalk/chalk-engine/src/logic.rs +cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --complete ../chalk/chalk-engine/src/logic.rs:94:0 ``` Look for `fn benchmark_xxx` tests for a quick way to reproduce performance problems. @@ -283,7 +283,8 @@ repository. We use the [rustc-josh-sync](https://github.com/rust-lang/josh-sync) repositories. You can find documentation of the tool [here](https://github.com/rust-lang/josh-sync). You can install the synchronization tool using the following commands: -``` + +```bash cargo install --locked --git https://github.com/rust-lang/josh-sync ``` diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/debugging.md b/src/tools/rust-analyzer/docs/book/src/contributing/debugging.md index fcda664f5ed35..ace9be025ab4f 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/debugging.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/debugging.md @@ -68,7 +68,7 @@ while d == 4 { // set a breakpoint here and change the value However for this to work, you will need to enable debug_assertions in your build -```rust +```bash RUSTFLAGS='--cfg debug_assertions' cargo build --release ``` diff --git a/src/tools/rust-analyzer/docs/book/src/installation.md b/src/tools/rust-analyzer/docs/book/src/installation.md index 3a4c0cf227749..cc636c31e6ec8 100644 --- a/src/tools/rust-analyzer/docs/book/src/installation.md +++ b/src/tools/rust-analyzer/docs/book/src/installation.md @@ -13,7 +13,9 @@ editor](./other_editors.html). rust-analyzer will attempt to install the standard library source code automatically. You can also install it manually with `rustup`. - $ rustup component add rust-src +```bash +rustup component add rust-src +``` Only the latest stable standard library source is officially supported for use with rust-analyzer. If you are using an older toolchain or have diff --git a/src/tools/rust-analyzer/docs/book/src/rust_analyzer_binary.md b/src/tools/rust-analyzer/docs/book/src/rust_analyzer_binary.md index c7ac3087ced74..2b62011a8e332 100644 --- a/src/tools/rust-analyzer/docs/book/src/rust_analyzer_binary.md +++ b/src/tools/rust-analyzer/docs/book/src/rust_analyzer_binary.md @@ -11,9 +11,11 @@ your `$PATH`. On Linux to install the `rust-analyzer` binary into `~/.local/bin`, these commands should work: - $ mkdir -p ~/.local/bin - $ curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer - $ chmod +x ~/.local/bin/rust-analyzer +```bash +mkdir -p ~/.local/bin +curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer +chmod +x ~/.local/bin/rust-analyzer +``` Make sure that `~/.local/bin` is listed in the `$PATH` variable and use the appropriate URL if you’re not on a `x86-64` system. @@ -24,8 +26,10 @@ or `/usr/local/bin` will work just as well. Alternatively, you can install it from source using the command below. You’ll need the latest stable version of the Rust toolchain. - $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer - $ cargo xtask install --server +```bash +git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer +cargo xtask install --server +``` If your editor can’t find the binary even though the binary is on your `$PATH`, the likely explanation is that it doesn’t see the same `$PATH` @@ -38,7 +42,9 @@ the environment should help. `rust-analyzer` is available in `rustup`: - $ rustup component add rust-analyzer +```bash +rustup component add rust-analyzer +``` ### Arch Linux @@ -53,7 +59,9 @@ User Repository): Install it with pacman, for example: - $ pacman -S rust-analyzer +```bash +pacman -S rust-analyzer +``` ### Gentoo Linux @@ -64,7 +72,9 @@ Install it with pacman, for example: The `rust-analyzer` binary can be installed via [Homebrew](https://brew.sh/). - $ brew install rust-analyzer +```bash +brew install rust-analyzer +``` ### Windows diff --git a/src/tools/rust-analyzer/docs/book/src/troubleshooting.md b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md index a357cbef415ce..c315bfad7f947 100644 --- a/src/tools/rust-analyzer/docs/book/src/troubleshooting.md +++ b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md @@ -37,13 +37,13 @@ bypassing LSP machinery. When filing issues, it is useful (but not necessary) to try to minimize examples. An ideal bug reproduction looks like this: -```shell -$ git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash -$ rust-analyzer --version +```bash +git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash +rust-analyzer --version rust-analyzer dd12184e4 2021-05-08 dev -$ rust-analyzer analysis-stats . -💀 💀 💀 +rust-analyzer analysis-stats . ``` +💀 💀 💀 It is especially useful when the `repo` doesn’t use external crates or the standard library. diff --git a/src/tools/rust-analyzer/docs/book/src/vs_code.md b/src/tools/rust-analyzer/docs/book/src/vs_code.md index 233b862d2c6b7..75f940e69a885 100644 --- a/src/tools/rust-analyzer/docs/book/src/vs_code.md +++ b/src/tools/rust-analyzer/docs/book/src/vs_code.md @@ -49,7 +49,9 @@ Alternatively, download a VSIX corresponding to your platform from the Install the extension with the `Extensions: Install from VSIX` command within VS Code, or from the command line via: - $ code --install-extension /path/to/rust-analyzer.vsix +```bash +code --install-extension /path/to/rust-analyzer.vsix +``` If you are running an unsupported platform, you can install `rust-analyzer-no-server.vsix` and compile or obtain a server binary. @@ -64,8 +66,10 @@ example: Both the server and the Code plugin can be installed from source: - $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer - $ cargo xtask install +```bash +git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer +cargo xtask install +``` You’ll need Cargo, nodejs (matching a supported version of VS Code) and npm for this. @@ -76,7 +80,9 @@ Remote, instead you’ll need to install the `.vsix` manually. If you’re not using Code, you can compile and install only the LSP server: - $ cargo xtask install --server +```bash +cargo xtask install --server +``` Make sure that `.cargo/bin` is in `$PATH` and precedes paths where `rust-analyzer` may also be installed. Specifically, `rustup` includes a @@ -118,4 +124,3 @@ steps might help: A C compiler should already be available via `org.freedesktop.Sdk`. Any other tools or libraries you will need to acquire from Flatpak. - diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs index b9f570fe0e329..205072a6ce0cf 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs @@ -112,7 +112,7 @@ const RESERVED: &[&str] = &[ // keywords that are keywords only in specific parse contexts #[doc(alias = "WEAK_KEYWORDS")] const CONTEXTUAL_KEYWORDS: &[&str] = - &["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet", "safe"]; + &["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet", "safe", "bikeshed"]; // keywords we use for special macro expansions const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[ "asm", diff --git a/tests/crashes/137190-2.rs b/tests/crashes/137190-2.rs deleted file mode 100644 index 0c68b5aa4a518..0000000000000 --- a/tests/crashes/137190-2.rs +++ /dev/null @@ -1,18 +0,0 @@ -//@ known-bug: #137190 -trait Supertrait { - fn method(&self) {} -} - -trait Trait

: Supertrait<()> {} - -impl

Trait

for () {} - -const fn upcast

(x: &dyn Trait

) -> &dyn Supertrait<()> { - x -} - -const fn foo() -> &'static dyn Supertrait<()> { - upcast::<()>(&()) -} - -const _: &'static dyn Supertrait<()> = foo(); diff --git a/tests/crashes/137190-3.rs b/tests/crashes/137190-3.rs deleted file mode 100644 index 88ae88e11bcdb..0000000000000 --- a/tests/crashes/137190-3.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ known-bug: #137190 -trait Supertrait { - fn method(&self) {} -} - -trait Trait: Supertrait {} - -impl Trait for () {} - -const _: &dyn Supertrait = &() as &dyn Trait as &dyn Supertrait; diff --git a/tests/crashes/137916.rs b/tests/crashes/137916.rs deleted file mode 100644 index b25e7b200d959..0000000000000 --- a/tests/crashes/137916.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ known-bug: #137916 -//@ edition: 2021 -use std::ptr::null; - -async fn a() -> Box { - Box::new(async { - let non_send = null::<()>(); - &non_send; - async {}.await - }) -} - -fn main() {} diff --git a/tests/crashes/138274.rs b/tests/crashes/138274.rs deleted file mode 100644 index d657b27e46f3e..0000000000000 --- a/tests/crashes/138274.rs +++ /dev/null @@ -1,18 +0,0 @@ -//@ known-bug: #138274 -//@ edition: 2021 -//@ compile-flags: --crate-type=lib -trait Trait {} - -fn foo() -> Box { - todo!() -} - -fn fetch() { - async { - let fut = async { - let _x = foo(); - async {}.await; - }; - let _: Box = Box::new(fut); - }; -} diff --git a/tests/run-make/wasm-emscripten-cdylib/foo.rs b/tests/run-make/wasm-emscripten-cdylib/foo.rs new file mode 100644 index 0000000000000..08ad8e7b5c616 --- /dev/null +++ b/tests/run-make/wasm-emscripten-cdylib/foo.rs @@ -0,0 +1,4 @@ +#[no_mangle] +pub extern "C" fn foo() -> i32 { + 42 +} diff --git a/tests/run-make/wasm-emscripten-cdylib/rmake.rs b/tests/run-make/wasm-emscripten-cdylib/rmake.rs new file mode 100644 index 0000000000000..ef5fc17c2bbe2 --- /dev/null +++ b/tests/run-make/wasm-emscripten-cdylib/rmake.rs @@ -0,0 +1,25 @@ +//! Check that cdylib crate type is supported for the wasm32-unknown-emscripten +//! target and produces a valid Emscripten dynamic library. + +//@ only-wasm32-unknown-emscripten + +use run_make_support::{bare_rustc, rfs, wasmparser}; + +fn main() { + bare_rustc().input("foo.rs").target("wasm32-unknown-emscripten").crate_type("cdylib").run(); + + // Verify the output is a valid wasm file with a dylink.0 section + let file = rfs::read("foo.wasm"); + let mut has_dylink = false; + + for payload in wasmparser::Parser::new(0).parse_all(&file) { + let payload = payload.unwrap(); + if let wasmparser::Payload::CustomSection(s) = payload { + if s.name() == "dylink.0" { + has_dylink = true; + } + } + } + + assert!(has_dylink, "expected dylink.0 section in emscripten cdylib output"); +} diff --git a/tests/ui/async-await/issue-70818.rs b/tests/ui/async-await/issue-70818.rs index c11332fe7d847..af9f33d9050b0 100644 --- a/tests/ui/async-await/issue-70818.rs +++ b/tests/ui/async-await/issue-70818.rs @@ -2,9 +2,8 @@ use std::future::Future; fn foo(ty: T, ty1: U) -> impl Future + Send { - //~^ ERROR future cannot be sent between threads safely + //~^ ERROR: future cannot be sent between threads safely async { (ty, ty1) } - //~^ ERROR future cannot be sent between threads safely } fn main() {} diff --git a/tests/ui/async-await/issue-70818.stderr b/tests/ui/async-await/issue-70818.stderr index 07fd20cdd77ea..8de6a825042bb 100644 --- a/tests/ui/async-await/issue-70818.stderr +++ b/tests/ui/async-await/issue-70818.stderr @@ -1,24 +1,3 @@ -error: future cannot be sent between threads safely - --> $DIR/issue-70818.rs:6:5 - | -LL | async { (ty, ty1) } - | ^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send` - | -note: captured value is not `Send` - --> $DIR/issue-70818.rs:6:18 - | -LL | async { (ty, ty1) } - | ^^^ has type `U` which is not `Send` -note: required by a bound in an opaque type - --> $DIR/issue-70818.rs:4:69 - | -LL | fn foo(ty: T, ty1: U) -> impl Future + Send { - | ^^^^ -help: consider restricting type parameter `U` with trait `Send` - | -LL | fn foo(ty: T, ty1: U) -> impl Future + Send { - | +++++++++++++++++++ - error: future cannot be sent between threads safely --> $DIR/issue-70818.rs:4:38 | @@ -35,5 +14,5 @@ help: consider restricting type parameter `U` with trait `Send` LL | fn foo(ty: T, ty1: U) -> impl Future + Send { | +++++++++++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/async-await/issue-70935-complex-spans.rs b/tests/ui/async-await/issue-70935-complex-spans.rs index 2851637ae78fd..27826f79dcd96 100644 --- a/tests/ui/async-await/issue-70935-complex-spans.rs +++ b/tests/ui/async-await/issue-70935-complex-spans.rs @@ -15,7 +15,6 @@ async fn baz(_c: impl FnMut() -> T) where T: Future { fn foo(x: NotSync) -> impl Future + Send { //~^ ERROR `*mut ()` cannot be shared between threads safely async move { - //~^ ERROR `*mut ()` cannot be shared between threads safely baz(|| async { foo(x.clone()); }).await; diff --git a/tests/ui/async-await/issue-70935-complex-spans.stderr b/tests/ui/async-await/issue-70935-complex-spans.stderr index 31d15c4592177..b9180c620b0dc 100644 --- a/tests/ui/async-await/issue-70935-complex-spans.stderr +++ b/tests/ui/async-await/issue-70935-complex-spans.stderr @@ -1,46 +1,3 @@ -error[E0277]: `*mut ()` cannot be shared between threads safely - --> $DIR/issue-70935-complex-spans.rs:17:5 - | -LL | / async move { -LL | | -LL | | baz(|| async { -LL | | foo(x.clone()); -LL | | }).await; -LL | | } - | |_____^ `*mut ()` cannot be shared between threads safely - | - = help: within `NotSync`, the trait `Sync` is not implemented for `*mut ()` -note: required because it appears within the type `PhantomData<*mut ()>` - --> $SRC_DIR/core/src/marker.rs:LL:COL -note: required because it appears within the type `NotSync` - --> $DIR/issue-70935-complex-spans.rs:9:8 - | -LL | struct NotSync(PhantomData<*mut ()>); - | ^^^^^^^ - = note: required for `&NotSync` to implement `Send` -note: required because it's used within this closure - --> $DIR/issue-70935-complex-spans.rs:19:13 - | -LL | baz(|| async { - | ^^ -note: required because it's used within this `async` fn body - --> $DIR/issue-70935-complex-spans.rs:12:67 - | -LL | async fn baz(_c: impl FnMut() -> T) where T: Future { - | ___________________________________________________________________^ -LL | | } - | |_^ -note: required because it's used within this `async` block - --> $DIR/issue-70935-complex-spans.rs:17:5 - | -LL | async move { - | ^^^^^^^^^^ -note: required by a bound in an opaque type - --> $DIR/issue-70935-complex-spans.rs:15:37 - | -LL | fn foo(x: NotSync) -> impl Future + Send { - | ^^^^ - error[E0277]: `*mut ()` cannot be shared between threads safely --> $DIR/issue-70935-complex-spans.rs:15:23 | @@ -57,7 +14,7 @@ LL | struct NotSync(PhantomData<*mut ()>); | ^^^^^^^ = note: required for `&NotSync` to implement `Send` note: required because it's used within this closure - --> $DIR/issue-70935-complex-spans.rs:19:13 + --> $DIR/issue-70935-complex-spans.rs:18:13 | LL | baz(|| async { | ^^ @@ -74,6 +31,6 @@ note: required because it's used within this `async` block LL | async move { | ^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/crashes/135470.rs b/tests/ui/coercion/vtable-impossible-predicates-async.rs similarity index 79% rename from tests/crashes/135470.rs rename to tests/ui/coercion/vtable-impossible-predicates-async.rs index efa017b5457cf..fe6aa9843fc61 100644 --- a/tests/crashes/135470.rs +++ b/tests/ui/coercion/vtable-impossible-predicates-async.rs @@ -1,4 +1,8 @@ -//@ known-bug: #135470 +// Regression test for #135470. +// Verify that we don't ICE when building vtable entries +// for a blanket impl involving async and impossible predicates. + +//@ check-pass //@ compile-flags: -Copt-level=0 //@ edition: 2021 diff --git a/tests/ui/coercion/vtable-unsatisfied-supertrait-generics.rs b/tests/ui/coercion/vtable-unsatisfied-supertrait-generics.rs new file mode 100644 index 0000000000000..41e603fc0a0a3 --- /dev/null +++ b/tests/ui/coercion/vtable-unsatisfied-supertrait-generics.rs @@ -0,0 +1,25 @@ +// Regression test for #137190. +// Variant of vtable-unsatisfied-supertrait.rs with generic traits. +// Verify that we don't ICE when building vtable entries +// for a generic trait whose supertrait is not implemented. + +//@ compile-flags: --crate-type lib + +trait Supertrait { + fn method(&self) {} +} + +trait Trait

: Supertrait<()> {} + +impl

Trait

for () {} +//~^ ERROR the trait bound `(): Supertrait<()>` is not satisfied + +const fn upcast

(x: &dyn Trait

) -> &dyn Supertrait<()> { + x +} + +const fn foo() -> &'static dyn Supertrait<()> { + upcast::<()>(&()) +} + +const _: &'static dyn Supertrait<()> = foo(); diff --git a/tests/ui/coercion/vtable-unsatisfied-supertrait-generics.stderr b/tests/ui/coercion/vtable-unsatisfied-supertrait-generics.stderr new file mode 100644 index 0000000000000..a485d2d539534 --- /dev/null +++ b/tests/ui/coercion/vtable-unsatisfied-supertrait-generics.stderr @@ -0,0 +1,20 @@ +error[E0277]: the trait bound `(): Supertrait<()>` is not satisfied + --> $DIR/vtable-unsatisfied-supertrait-generics.rs:14:22 + | +LL | impl

Trait

for () {} + | ^^ the trait `Supertrait<()>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/vtable-unsatisfied-supertrait-generics.rs:8:1 + | +LL | trait Supertrait { + | ^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `Trait` + --> $DIR/vtable-unsatisfied-supertrait-generics.rs:12:17 + | +LL | trait Trait

: Supertrait<()> {} + | ^^^^^^^^^^^^^^ required by this bound in `Trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coercion/vtable-unsatisfied-supertrait.rs b/tests/ui/coercion/vtable-unsatisfied-supertrait.rs new file mode 100644 index 0000000000000..26a83d2fb0661 --- /dev/null +++ b/tests/ui/coercion/vtable-unsatisfied-supertrait.rs @@ -0,0 +1,16 @@ +// Regression test for #137190. +// Verify that we don't ICE when building vtable entries +// for a trait whose supertrait is not implemented. + +//@ compile-flags: --crate-type lib + +trait Supertrait { + fn method(&self) {} +} + +trait Trait: Supertrait {} + +impl Trait for () {} +//~^ ERROR the trait bound `(): Supertrait` is not satisfied + +const _: &dyn Supertrait = &() as &dyn Trait as &dyn Supertrait; diff --git a/tests/ui/coercion/vtable-unsatisfied-supertrait.stderr b/tests/ui/coercion/vtable-unsatisfied-supertrait.stderr new file mode 100644 index 0000000000000..7df2c95c7facc --- /dev/null +++ b/tests/ui/coercion/vtable-unsatisfied-supertrait.stderr @@ -0,0 +1,20 @@ +error[E0277]: the trait bound `(): Supertrait` is not satisfied + --> $DIR/vtable-unsatisfied-supertrait.rs:13:16 + | +LL | impl Trait for () {} + | ^^ the trait `Supertrait` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/vtable-unsatisfied-supertrait.rs:7:1 + | +LL | trait Supertrait { + | ^^^^^^^^^^^^^^^^ +note: required by a bound in `Trait` + --> $DIR/vtable-unsatisfied-supertrait.rs:11:14 + | +LL | trait Trait: Supertrait {} + | ^^^^^^^^^^ required by this bound in `Trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/issue-105084.rs b/tests/ui/coroutine/issue-105084.rs index cddee49901757..4e9f6ee3a5958 100644 --- a/tests/ui/coroutine/issue-105084.rs +++ b/tests/ui/coroutine/issue-105084.rs @@ -36,7 +36,6 @@ fn main() { // one inside `g` and one inside `h`. // Proceed and drop `t` in `g`. Pin::new(&mut g).resume(()); - //~^ ERROR borrow of moved value: `g` // Proceed and drop `t` in `h` -> double free! Pin::new(&mut h).resume(()); diff --git a/tests/ui/coroutine/issue-105084.stderr b/tests/ui/coroutine/issue-105084.stderr index 23c1fdc545922..a69d20e607a42 100644 --- a/tests/ui/coroutine/issue-105084.stderr +++ b/tests/ui/coroutine/issue-105084.stderr @@ -1,27 +1,3 @@ -error[E0382]: borrow of moved value: `g` - --> $DIR/issue-105084.rs:38:14 - | -LL | let mut g = #[coroutine] - | ----- move occurs because `g` has type `{coroutine@$DIR/issue-105084.rs:16:5: 16:7}`, which does not implement the `Copy` trait -... -LL | let mut h = copy(g); - | - value moved here -... -LL | Pin::new(&mut g).resume(()); - | ^^^^^^ value borrowed here after move - | -note: consider changing this parameter type in function `copy` to borrow instead if owning the value isn't necessary - --> $DIR/issue-105084.rs:10:21 - | -LL | fn copy(x: T) -> T { - | ---- ^ this parameter takes ownership of the value - | | - | in this function -help: consider cloning the value if the performance cost is acceptable - | -LL | let mut h = copy(g.clone()); - | ++++++++ - error[E0277]: the trait bound `Box<(i32, ())>: Copy` is not satisfied in `{coroutine@$DIR/issue-105084.rs:16:5: 16:7}` --> $DIR/issue-105084.rs:32:17 | @@ -45,7 +21,6 @@ note: required by a bound in `copy` LL | fn copy(x: T) -> T { | ^^^^ required by this bound in `copy` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0277, E0382. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coroutine/stalled-coroutine-obligations.rs b/tests/ui/coroutine/stalled-coroutine-obligations.rs new file mode 100644 index 0000000000000..89af3c9a583c2 --- /dev/null +++ b/tests/ui/coroutine/stalled-coroutine-obligations.rs @@ -0,0 +1,32 @@ +//@ edition: 2021 + +// Regression tests for #137916 and #138274 +// We now check stalled coroutine obligations eagerly at the start of `mir_borrowck`. +// So these unsatisfied bounds are caught before causing ICEs. +use std::ptr::null; + +async fn a() -> Box { + Box::new(async { + //~^ ERROR: future cannot be sent between threads safely + let non_send = null::<()>(); + &non_send; + async {}.await + }) +} + + +trait Trait {} +fn foo() -> Box { todo!() } + +fn fetch() { + async { + let fut = async { + let _x = foo(); + async {}.await; + }; + let _: Box = Box::new(fut); + //~^ ERROR: future cannot be sent between threads safely + }; +} + +fn main() {} diff --git a/tests/ui/coroutine/stalled-coroutine-obligations.stderr b/tests/ui/coroutine/stalled-coroutine-obligations.stderr new file mode 100644 index 0000000000000..cbf395dd6cfb3 --- /dev/null +++ b/tests/ui/coroutine/stalled-coroutine-obligations.stderr @@ -0,0 +1,40 @@ +error: future cannot be sent between threads safely + --> $DIR/stalled-coroutine-obligations.rs:9:5 + | +LL | / Box::new(async { +LL | | +LL | | let non_send = null::<()>(); +LL | | &non_send; +LL | | async {}.await +LL | | }) + | |______^ future created by async block is not `Send` + | + = help: within `{async block@$DIR/stalled-coroutine-obligations.rs:9:14: 9:19}`, the trait `Send` is not implemented for `*const ()` +note: future is not `Send` as this value is used across an await + --> $DIR/stalled-coroutine-obligations.rs:13:18 + | +LL | let non_send = null::<()>(); + | -------- has type `*const ()` which is not `Send` +LL | &non_send; +LL | async {}.await + | ^^^^^ await occurs here, with `non_send` maybe used later + = note: required for the cast from `Box<{async block@$DIR/stalled-coroutine-obligations.rs:9:14: 9:19}>` to `Box` + +error: future cannot be sent between threads safely + --> $DIR/stalled-coroutine-obligations.rs:27:32 + | +LL | let _: Box = Box::new(fut); + | ^^^^^^^^^^^^^ future created by async block is not `Send` + | + = help: the trait `Send` is not implemented for `dyn Trait` +note: future is not `Send` as this value is used across an await + --> $DIR/stalled-coroutine-obligations.rs:25:22 + | +LL | let _x = foo(); + | -- has type `Box` which is not `Send` +LL | async {}.await; + | ^^^^^ await occurs here, with `_x` maybe used later + = note: required for the cast from `Box<{async block@$DIR/stalled-coroutine-obligations.rs:23:19: 23:24}>` to `Box` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/suggestions/issue-71394-no-from-impl.stderr b/tests/ui/suggestions/issue-71394-no-from-impl.stderr index 9e068c311ae05..b8a240f7f07c5 100644 --- a/tests/ui/suggestions/issue-71394-no-from-impl.stderr +++ b/tests/ui/suggestions/issue-71394-no-from-impl.stderr @@ -13,7 +13,7 @@ LL | let _: &[i8] = data.into(); `[T; 6]` implements `From<(T, T, T, T, T, T)>` `[T; 7]` implements `From<(T, T, T, T, T, T, T)>` `[T; 8]` implements `From<(T, T, T, T, T, T, T, T)>` - and 6 others + and 7 others = note: required for `&[u8]` to implement `Into<&[i8]>` error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/stalled-coroutine-obligations.rs b/tests/ui/traits/next-solver/stalled-coroutine-obligations.rs new file mode 100644 index 0000000000000..7c789af9e4ccd --- /dev/null +++ b/tests/ui/traits/next-solver/stalled-coroutine-obligations.rs @@ -0,0 +1,50 @@ +//@ edition: 2024 +//@ compile-flags: -Znext-solver --diagnostic-width=300 + +// Previously we check stalled coroutine obligations after borrowck pass. +// And we wrongly assume that these obligations hold in borrowck which leads to +// silent normalization failures. +// In the next solver, we register opaques types via `NormalizesTo` goals. +// So these failures also cause those opaques types not registered in storage. +// +// Regression test for #151322 and #151323. + +#![feature(type_alias_impl_trait)] +#![feature(negative_impls)] +#![feature(auto_traits)] + +fn stalled_copy_clone() { + type T = impl Copy; + let foo: T = async {}; + //~^ ERROR: the trait bound + + type U = impl Clone; + let bar: U = async {}; + //~^ ERROR: the trait bound +} + +auto trait Valid {} +struct False; +impl !Valid for False {} + +fn stalled_auto_traits() { + type T = impl Valid; + let a = False; + let foo: T = async { a }; + //~^ ERROR: the trait bound `False: Valid` is not satisfied +} + + +trait Trait { + fn stalled_send(&self, b: *mut ()) -> impl Future + Send { + //~^ ERROR: type mismatch resolving + //~| ERROR: type mismatch resolving + async move { + //~^ ERROR: type mismatch resolving + b + } + } +} + + +fn main() {} diff --git a/tests/ui/traits/next-solver/stalled-coroutine-obligations.stderr b/tests/ui/traits/next-solver/stalled-coroutine-obligations.stderr new file mode 100644 index 0000000000000..6afa406bf367e --- /dev/null +++ b/tests/ui/traits/next-solver/stalled-coroutine-obligations.stderr @@ -0,0 +1,62 @@ +error[E0277]: the trait bound `{async block@$DIR/stalled-coroutine-obligations.rs:18:18: 18:23}: Copy` is not satisfied + --> $DIR/stalled-coroutine-obligations.rs:18:14 + | +LL | let foo: T = async {}; + | ^ the trait `Copy` is not implemented for `{async block@$DIR/stalled-coroutine-obligations.rs:18:18: 18:23}` + +error[E0277]: the trait bound `{async block@$DIR/stalled-coroutine-obligations.rs:22:18: 22:23}: Clone` is not satisfied + --> $DIR/stalled-coroutine-obligations.rs:22:14 + | +LL | let bar: U = async {}; + | ^ the trait `Clone` is not implemented for `{async block@$DIR/stalled-coroutine-obligations.rs:22:18: 22:23}` + +error[E0277]: the trait bound `False: Valid` is not satisfied in `{async block@$DIR/stalled-coroutine-obligations.rs:33:18: 33:23}` + --> $DIR/stalled-coroutine-obligations.rs:33:14 + | +LL | let foo: T = async { a }; + | ^ ----- within this `{async block@$DIR/stalled-coroutine-obligations.rs:33:18: 33:23}` + | | + | unsatisfied trait bound + | +help: within `{async block@$DIR/stalled-coroutine-obligations.rs:33:18: 33:23}`, the trait `Valid` is not implemented for `False` + --> $DIR/stalled-coroutine-obligations.rs:27:1 + | +LL | struct False; + | ^^^^^^^^^^^^ +note: captured value does not implement `Valid` + --> $DIR/stalled-coroutine-obligations.rs:33:26 + | +LL | let foo: T = async { a }; + | ^ has type `False` which does not implement `Valid` + +error[E0271]: type mismatch resolving `impl Future + Send == {async block@$DIR/stalled-coroutine-obligations.rs:42:9: 42:19}` + --> $DIR/stalled-coroutine-obligations.rs:39:5 + | +LL | fn stalled_send(&self, b: *mut ()) -> impl Future + Send { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + +error[E0271]: type mismatch resolving `impl Future + Send == {async block@$DIR/stalled-coroutine-obligations.rs:42:9: 42:19}` + --> $DIR/stalled-coroutine-obligations.rs:42:9 + | +LL | / async move { +LL | | +LL | | b +LL | | } + | |_________^ types differ + +error[E0271]: type mismatch resolving `{async block@$DIR/stalled-coroutine-obligations.rs:42:9: 42:19} <: impl Future + Send` + --> $DIR/stalled-coroutine-obligations.rs:39:62 + | +LL | fn stalled_send(&self, b: *mut ()) -> impl Future + Send { + | ______________________________________________________________^ +LL | | +LL | | +LL | | async move { +... | +LL | | } + | |_____^ types differ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0271, E0277. +For more information about an error, try `rustc --explain E0271`.