diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 6a34bd4884d73..16c6fed9e9a42 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -27,7 +27,7 @@ use relate::lattice::{LatticeOp, LatticeOpKind}; use rustc_middle::ty::relate::solver_relating::RelateExt as NextSolverRelate; -use rustc_middle::ty::{Const, TypingMode}; +use rustc_middle::ty::{Const, ParamEnv, TypingMode}; use rustc_span::bug; use super::*; @@ -124,6 +124,143 @@ impl<'tcx> InferCtxt<'tcx> { forked.inner.borrow_mut().projection_cache().clear(); forked } + + /// Makes `actual <: expected`. For example, if type-checking a + /// call like `foo(x)`, where `foo: fn(i32)`, you might have + /// `sup(i32, x)`, since the "expected" type is the type that + /// appears in the signature. + /// + /// If possible, prefer the wrapper methods on `ObligationCtxt`, + /// `FnCtxt`, or `TypeChecker`. + pub fn sup_raw( + self, + cause: &ObligationCause<'tcx>, + param_env: ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + self.at(cause, param_env).sup(define_opaque_types, expected, actual) + } + + /// Makes `expected <: actual`. + /// + /// If possible, prefer the wrapper methods on `ObligationCtxt`, + /// `FnCtxt`, or `TypeChecker`. + pub fn sub_raw( + self, + cause: &ObligationCause<'tcx>, + param_env: ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + self.at(cause, param_env).sub(define_opaque_types, expected, actual) + } + + /// Makes `expected == actual`. + /// + /// If possible, prefer the wrapper methods on `ObligationCtxt`, + /// `FnCtxt`, or `TypeChecker`. + pub fn eq_raw( + self, + cause: &ObligationCause<'tcx>, + param_env: ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + self.eq_trace_raw( + cause, + param_env, + define_opaque_types, + ToTrace::to_trace(cause, expected, actual), + expected, + actual, + ) + } + + /// Makes `expected == actual`. + /// + /// If possible, prefer the wrapper methods on `ObligationCtxt`, + /// `FnCtxt`, or `TypeChecker`. + pub fn eq_trace_raw( + self, + cause: &ObligationCause<'tcx>, + param_env: ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + trace: TypeTrace<'tcx>, + expected: T, + actual: T, + ) -> InferResult<'tcx, ()> + where + T: Relate>, + { + self.at(cause, param_env).eq_trace(define_opaque_types, trace, expected, actual) + } + + /// If possible, prefer the wrapper methods on `ObligationCtxt`, + /// `FnCtxt`, or `TypeChecker`. + pub fn relate_raw( + self, + cause: &ObligationCause<'tcx>, + param_env: ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + expected: T, + variance: ty::Variance, + actual: T, + ) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + self.at(cause, param_env).relate(define_opaque_types, expected, variance, actual) + } + + /// Computes the least-upper-bound, or mutual supertype, of two + /// values. The order of the arguments doesn't matter, but since + /// this can result in an error (e.g., if asked to compute LUB of + /// u32 and i32), it is meaningful to call one of them the + /// "expected type". + /// + /// If possible, prefer the wrapper methods on `ObligationCtxt`, + /// `FnCtxt`, or `TypeChecker`. + pub fn lub_raw( + self, + cause: &ObligationCause<'tcx>, + param_env: ParamEnv<'tcx>, + expected: T, + actual: T, + ) -> InferResult<'tcx, T> + where + T: ToTrace<'tcx>, + { + self.at(cause, param_env).lub(expected, actual) + } + + pub fn goals_to_obligations( + &self, + cause: &ObligationCause<'tcx>, + goals: Vec>>, + ) -> InferOk<'tcx, ()> { + InferOk { + value: (), + obligations: goals + .into_iter() + .map(|goal| { + Obligation::new(self.tcx, cause.clone(), goal.param_env, goal.predicate) + }) + .collect(), + } + } } pub trait ToTrace<'tcx>: Relate> + Copy { @@ -135,6 +272,8 @@ impl<'a, 'tcx> At<'a, 'tcx> { /// call like `foo(x)`, where `foo: fn(i32)`, you might have /// `sup(i32, x)`, since the "expected" type is the type that /// appears in the signature. + /// + /// FIXME: To be removed in favor of the [`InferCtxt::sup_raw`] method instead. pub fn sup( self, define_opaque_types: DefineOpaqueTypes, @@ -168,6 +307,8 @@ impl<'a, 'tcx> At<'a, 'tcx> { } /// Makes `expected <: actual`. + /// + /// FIXME: To be removed in favor of the [`InferCtxt::sub_raw`] method instead. pub fn sub( self, define_opaque_types: DefineOpaqueTypes, @@ -201,6 +342,8 @@ impl<'a, 'tcx> At<'a, 'tcx> { } /// Makes `expected == actual`. + /// + /// FIXME: To be removed in favor of the [`InferCtxt::eq_raw`] method instead. pub fn eq( self, define_opaque_types: DefineOpaqueTypes, @@ -219,6 +362,8 @@ impl<'a, 'tcx> At<'a, 'tcx> { } /// Makes `expected == actual`. + /// + /// FIXME: To be removed in favor of the [`InferCtxt::eq_trace_raw`] method instead. pub fn eq_trace( self, define_opaque_types: DefineOpaqueTypes, @@ -252,6 +397,7 @@ impl<'a, 'tcx> At<'a, 'tcx> { } } + /// FIXME: To be removed in favor of the [`InferCtxt::relate_raw`] method instead. pub fn relate( self, define_opaque_types: DefineOpaqueTypes, @@ -281,6 +427,8 @@ impl<'a, 'tcx> At<'a, 'tcx> { /// this can result in an error (e.g., if asked to compute LUB of /// u32 and i32), it is meaningful to call one of them the /// "expected type". + /// + /// FIXME: To be removed in favor of the [`InferCtxt::lub_raw`] method instead. pub fn lub(self, expected: T, actual: T) -> InferResult<'tcx, T> where T: ToTrace<'tcx>, @@ -295,6 +443,7 @@ impl<'a, 'tcx> At<'a, 'tcx> { Ok(InferOk { value, obligations: op.into_obligations() }) } + /// FIXME: To be removed in favor of the [`InferCtxt::goals_to_obligations`] method instead. fn goals_to_obligations( &self, goals: Vec>>, diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index e43d2c7f10b07..5127882d0b2b8 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -796,6 +796,7 @@ bidirectional_lang_item_map! { FnMut, FnOnce, FnPtrTrait, + Freeze, FusedIterator, Future, Iterator, diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 24e321fc68c67..630e1dd68e041 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -2,10 +2,13 @@ use std::fmt::Debug; use std::ops::Deref; use rustc_type_ir::solve::{ - Certainty, ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, NoSolution, + ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, NoSolution, VisibleForLeakCheck, }; -use rustc_type_ir::{self as ty, CanonicalizerState, Const, InferCtxtLike, Interner, TypeFoldable}; +use rustc_type_ir::transmute::Answer; +use rustc_type_ir::{ + self as ty, CanonicalizerState, Const, InferCtxtLike, Interner, Region, TypeFoldable, +}; /// `SolverDelegate` is one of the two traits in the `rustc_type_ir` shared abstraction layer /// between rustc and rust-analyzer abstracting over the [InferCtxt][inferctxt-doc], which had to be @@ -113,7 +116,7 @@ pub trait SolverDelegate: Deref + Sized { src: ::Ty, dst: ::Ty, assume: Const, - ) -> Result; + ) -> Result, ::Ty>, NoSolution>; /// Obtain canonicalizer state, either by allocating it afresh (the default) or by reusing /// previously allocated state. diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index f97bc0bbeb626..3b372876f574b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -16,6 +16,7 @@ use rustc_type_ir::solve::{ NoSolutionOrRerunNonErased, OpaqueTypesJank, QueryResultOrRerunNonErased, RerunCondition, RerunNonErased, RerunReason, RerunResultExt, SmallCopySet, TyOrConstInferVar, }; +use rustc_type_ir::transmute::Answer; use rustc_type_ir::{ self as ty, ClauseKind, Const, InferCtxtLike, Interner, MayBeErased, OpaqueTypeKey, PredicateKind, PredicateProxy, Region, RegionVid, TypeFoldable, TypeSuperVisitable, @@ -1565,7 +1566,7 @@ where src: I::Ty, dst: I::Ty, assume: Const, - ) -> Result { + ) -> Result, I::Ty>, NoSolution> { self.delegate.is_transmutable(dst, src, assume) } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 9ed4963577458..c9da77eced6d3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -22,8 +22,11 @@ mod trait_goals; use derive_where::derive_where; use rustc_type_ir::inherent::*; +use rustc_type_ir::lang_items::SolverTraitLangItem; pub use rustc_type_ir::solve::*; -use rustc_type_ir::{self as ty, Const, Interner, Region, TypeVisitableExt}; +use rustc_type_ir::transmute::Condition; +use rustc_type_ir::{self as ty, Const, Interner, Region, TraitClause, TypeVisitableExt, Upcast}; +use thin_vec::{ThinVec, thin_vec}; use tracing::instrument; pub use self::eval_ctxt::{ @@ -491,3 +494,42 @@ pub struct GoalEvaluation { /// before rerunning it. pub stalled_on: Option>, } + +/// Flatten the `Condition` tree into a conjunction of predicates. +#[instrument(level = "debug", skip(interner, predicate))] +pub fn flatten_answer_tree( + interner: I, + predicate: TraitClause, + cond: Condition, I::Ty>, +) -> ThinVec { + match cond { + // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll` + // Not possible until the trait solver supports disjunctions of obligations + Condition::IfAll(conds) | Condition::IfAny(conds) => conds + .into_iter() + .flat_map(|cond| flatten_answer_tree(interner, predicate, cond)) + .collect(), + Condition::Immutable { ty } => { + let trait_ref = ty::TraitRef::new( + interner, + interner.require_trait_lang_item(SolverTraitLangItem::Freeze), + [I::GenericArg::from(ty)], + ); + thin_vec![trait_ref.upcast(interner)] + } + Condition::Outlives { long, short } => { + let outlives = ty::OutlivesClause(long, short); + thin_vec![outlives.upcast(interner)] + } + Condition::Transmutable { src, dst } => { + let transmute_trait = predicate.def_id(); + let assume = predicate.trait_ref.args.const_at(2); + let trait_ref = ty::TraitRef::new( + interner, + transmute_trait, + [I::GenericArg::from(dst), I::GenericArg::from(src), I::GenericArg::from(assume)], + ); + thin_vec![trait_ref.upcast(interner)] + } + } +} diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 14f1eb939b2db..1d3008da399dc 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -24,7 +24,7 @@ use crate::solve::assembly::{ use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, - MergeCandidateInfo, NoSolution, ParamEnvSource, StalledOnCoroutines, + MergeCandidateInfo, NoSolution, ParamEnvSource, StalledOnCoroutines, flatten_answer_tree, has_only_region_constraints, }; @@ -659,6 +659,8 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, ) -> Result, NoSolutionOrRerunNonErased> { + use rustc_type_ir::transmute::Answer; + if goal.predicate.polarity != ty::ClausePolarity::Positive { return Err(NoSolution.into()); } @@ -681,11 +683,28 @@ where goal.predicate.trait_ref.args.const_at(2), )?; - let certainty = ecx.is_transmutable( + let answer = ecx.is_transmutable( goal.predicate.trait_ref.args.type_at(0), goal.predicate.trait_ref.args.type_at(1), assume, )?; + + let certainty = match answer { + Answer::No(_) => return Err(NoSolution.into()), + Answer::Yes => Certainty::Yes, + Answer::If(cond) => { + let tcx = ecx.cx(); + ecx.add_goals( + GoalSource::Misc, + flatten_answer_tree(tcx, goal.predicate, cond) + .into_iter() + .map(|predicate| Goal::new(tcx, goal.param_env, predicate)), + )?; + + Certainty::Yes + } + }; + ecx.evaluate_added_goals_and_make_canonical_response(certainty) }, ) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 29f6ef92045e6..6aa5143ddcb22 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -31,6 +31,7 @@ use rustc_middle::ty::print::{ PrintPolyTraitClauseExt, PrintPolyTraitRefExt as _, PrintTraitClauseExt as _, PrintTraitRefExt as _, with_forced_trimmed_paths, }; +use rustc_middle::ty::transmute::{Answer, Reason}; use rustc_middle::ty::{ self, GenericArgKind, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, Unnormalized, Upcast, @@ -3253,7 +3254,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_ref.args.type_at(0), assume, ), - rustc_transmute::Answer::Yes, + Answer::Yes, ); // If the normalized check unexpectedly passes, fall back to root obligation for reporting. @@ -3274,7 +3275,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_pred: ty::PolyTraitClause<'tcx>, span: Span, ) -> GetSafeTransmuteErrorAndReason { - use rustc_transmute::Answer; self.probe(|_| { // We don't assemble a transmutability candidate for types that are generic // and we should have ambiguity for types that still have non-region infer. @@ -3311,69 +3311,61 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { Answer::No(reason) => { let safe_transmute_explanation = match reason { - rustc_transmute::Reason::SrcIsNotYetSupported => { + Reason::SrcIsNotYetSupported => { format!("analyzing the transmutability of `{src}` is not yet supported") } - rustc_transmute::Reason::DstIsNotYetSupported => { + Reason::DstIsNotYetSupported => { format!("analyzing the transmutability of `{dst}` is not yet supported") } - rustc_transmute::Reason::DstIsBitIncompatible => { + Reason::DstIsBitIncompatible => { format!( "at least one value of `{src}` isn't a bit-valid value of `{dst}`" ) } - rustc_transmute::Reason::DstUninhabited => { + Reason::DstUninhabited => { format!("`{dst}` is uninhabited") } - rustc_transmute::Reason::DstMayHaveSafetyInvariants => { + Reason::DstMayHaveSafetyInvariants => { format!("`{dst}` may carry safety invariants") } - rustc_transmute::Reason::DstIsTooBig => { + Reason::DstIsTooBig => { format!("the size of `{src}` is smaller than the size of `{dst}`") } - rustc_transmute::Reason::DstRefIsTooBig { - src, - src_size, - dst, - dst_size, - } => { + Reason::DstRefIsTooBig { src, src_size, dst, dst_size } => { format!( "the size of `{src}` ({src_size} bytes) \ is smaller than that of `{dst}` ({dst_size} bytes)" ) } - rustc_transmute::Reason::SrcSizeOverflow => { + Reason::SrcSizeOverflow => { format!( "values of the type `{src}` are too big for the target architecture" ) } - rustc_transmute::Reason::DstSizeOverflow => { + Reason::DstSizeOverflow => { format!( "values of the type `{dst}` are too big for the target architecture" ) } - rustc_transmute::Reason::DstHasStricterAlignment { - src_min_align, - dst_min_align, - } => { + Reason::DstHasStricterAlignment { src_min_align, dst_min_align } => { format!( "the minimum alignment of `{src}` ({src_min_align}) should be \ greater than that of `{dst}` ({dst_min_align})" ) } - rustc_transmute::Reason::DstIsMoreUnique => { + Reason::DstIsMoreUnique => { format!( "`{src}` is a shared reference, but `{dst}` is a unique reference" ) } // Already reported by rustc - rustc_transmute::Reason::TypeError => { + Reason::TypeError => { return GetSafeTransmuteErrorAndReason::Silent; } - rustc_transmute::Reason::SrcLayoutUnknown => { + Reason::SrcLayoutUnknown => { format!("`{src}` has an unknown layout") } - rustc_transmute::Reason::DstLayoutUnknown => { + Reason::DstLayoutUnknown => { format!("`{dst}` has an unknown layout") } }; diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 3e0796bd1f585..2335868a17b40 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -19,15 +19,17 @@ use rustc_infer::traits::solve::{ }; use rustc_lint_defs::builtin::RECURSION_DEPTH_EXCEEDING_LIMIT; use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::solve::{Certainty, MaybeInfo}; +use rustc_middle::traits::solve::MaybeInfo; use rustc_middle::ty::print::{FmtPrinter, Print}; +use rustc_middle::ty::transmute::Answer; use rustc_middle::ty::{ - self, CanonicalizerState, MayBeErased, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, + self, CanonicalizerState, MayBeErased, Region, Ty, TyCtxt, TypeFlags, TypeFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques, TyOrConstInferVar}; use rustc_span::{DUMMY_SP, Span}; use rustc_structures::Limit; +use rustc_transmute::TransmuteTypeEnv; use thin_vec::{ThinVec, thin_vec}; use super::inspect::InferCtxtProofTreeExt; @@ -491,20 +493,14 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< src: Ty<'tcx>, dst: Ty<'tcx>, assume: ty::Const<'tcx>, - ) -> Result { + ) -> Result, Ty<'tcx>>, NoSolution> { // Erase regions because we compute layouts in `rustc_transmute`, // which will ICE for region vars. let (dst, src) = self.tcx.erase_and_anonymize_regions((dst, src)); - let Some(assume) = rustc_transmute::Assume::from_const(self.tcx, assume) else { - return Err(NoSolution); - }; - - // FIXME(transmutability): This really should be returning nested goals for `Answer::If*` - match rustc_transmute::TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume) { - rustc_transmute::Answer::Yes => Ok(Certainty::Yes), - rustc_transmute::Answer::No(_) | rustc_transmute::Answer::If(_) => Err(NoSolution), - } + rustc_transmute::Assume::from_const(self.tcx, assume) + .map(|assume| TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume)) + .ok_or(NoSolution) } fn obtain_canonicalizer_state(&self) -> CanonicalizerState { diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 81517955181ee..6d5cddde61076 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -13,9 +13,8 @@ use rustc_attr_ir::lang_items::LangItem; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk}; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; -use rustc_middle::ty::{ - self, GenericArgsRef, Region, SizedTraitKind, Ty, TyCtxt, Unnormalized, Upcast, -}; +use rustc_middle::ty::{self, GenericArgsRef, SizedTraitKind, Ty, Unnormalized, Upcast}; +use rustc_next_trait_solver::solve::flatten_answer_tree; use rustc_span::def_id::DefId; use rustc_span::{bug, span_bug}; use thin_vec::thin_vec; @@ -285,69 +284,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result, SelectionError<'tcx>> { - use rustc_transmute::{Answer, Assume, Condition}; - - /// Flatten the `Condition` tree into a conjunction of obligations. - #[instrument(level = "debug", skip(tcx, obligation))] - fn flatten_answer_tree<'tcx>( - tcx: TyCtxt<'tcx>, - obligation: &PolyTraitObligation<'tcx>, - cond: Condition, Ty<'tcx>>, - assume: Assume, - ) -> PredicateObligations<'tcx> { - match cond { - // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll` - // Not possible until the trait solver supports disjunctions of obligations - Condition::IfAll(conds) | Condition::IfAny(conds) => conds - .into_iter() - .flat_map(|cond| flatten_answer_tree(tcx, obligation, cond, assume)) - .collect(), - Condition::Immutable { ty } => { - let trait_ref = ty::TraitRef::new( - tcx, - tcx.require_lang_item(LangItem::Freeze, obligation.cause.span), - [ty::GenericArg::from(ty)], - ); - thin_vec![Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - trait_ref, - )] - } - Condition::Outlives { long, short } => { - let outlives = ty::OutlivesClause(long, short); - thin_vec![Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - outlives, - )] - } - Condition::Transmutable { src, dst } => { - let transmute_trait = obligation.predicate.def_id(); - let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2); - let trait_ref = ty::TraitRef::new( - tcx, - transmute_trait, - [ - ty::GenericArg::from(dst), - ty::GenericArg::from(src), - ty::GenericArg::from(assume), - ], - ); - thin_vec![Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - trait_ref, - )] - } - } - } + use rustc_middle::ty::transmute::Answer; let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); @@ -367,9 +304,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let maybe_transmutable = transmute_env.is_transmutable(src, dst, assume); let fully_flattened = match maybe_transmutable { - Answer::No(_) => Err(SelectionError::Unimplemented)?, - Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, cond, assume), + Answer::No(_) => return Err(SelectionError::Unimplemented), Answer::Yes => PredicateObligations::new(), + Answer::If(cond) => { + flatten_answer_tree(self.tcx(), obligation.predicate.skip_binder(), cond) + .into_iter() + .map(|predicate| { + Obligation::new( + self.infcx.tcx, + obligation.cause.clone(), + obligation.param_env, + predicate, + ) + }) + .collect() + } }; debug!(?fully_flattened); diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index a7b07529f0bd1..c0ffc8babc723 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -3,8 +3,8 @@ //! The analysis converts compiler layouts into trees of bytes, references, and //! definition markers. It prunes destination paths that may carry safety invariants unless //! safety is assumed, then converts the trees into deterministic finite automata. -//! Comparing the automata produces an [`Answer`]; reference transitions can leave -//! [`Condition`]s for the trait solver to discharge. +//! Comparing the automata produces an [`rustc_middle::ty::transmute::Answer`]; reference transitions can leave +//! [`rustc_middle::ty::transmute::Condition`]s for the trait solver to discharge. // tidy-alphabetical-start #![cfg_attr(test, feature(test))] @@ -28,82 +28,11 @@ pub struct Assume { pub validity: bool, } -/// The result of a transmutability query under the supplied [`Assume`] options. -#[derive(Debug, Hash, Eq, PartialEq, Clone)] -pub enum Answer { - /// The analysis requires no further conditions. - Yes, - /// The analysis could not establish transmutability. - No(Reason), - /// Transmutability depends on conditions that the trait solver must discharge. - If(Condition), -} - -/// A condition which must hold for safe transmutation to be possible. -#[derive(Debug, Hash, Eq, PartialEq, Clone)] -pub enum Condition { - /// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`. - Transmutable { src: T, dst: T }, - - /// The region `long` must outlive `short`. - Outlives { long: R, short: R }, - - /// The type `ty` must satisfy `Freeze`. - Immutable { ty: T }, - - /// `Src` is transmutable into `Dst`, if all of the enclosed requirements are met. - IfAll(Vec>), - - /// `Src` is transmutable into `Dst` if any of the enclosed requirements are met. - IfAny(Vec>), -} - -/// Answers "why wasn't the source type transmutable into the destination type?" -#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)] -pub enum Reason { - /// The layout of the source type is not yet supported. - SrcIsNotYetSupported, - /// The layout of the destination type is not yet supported. - DstIsNotYetSupported, - /// The layout of the destination type is bit-incompatible with the source type. - DstIsBitIncompatible, - /// The destination type is uninhabited. - DstUninhabited, - /// The destination type may carry safety invariants. - DstMayHaveSafetyInvariants, - /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized. - DstIsTooBig, - /// The destination referent is larger than the source referent. - DstRefIsTooBig { - /// The referent of the source type. - src: T, - /// The size of the source type's referent. - src_size: usize, - /// The too-large referent of the destination type. - dst: T, - /// The size of the destination type's referent. - dst_size: usize, - }, - /// The destination referent requires stricter alignment than the source referent. - DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize }, - /// Can't go from shared pointer to unique pointer - DstIsMoreUnique, - /// Encountered a type error - TypeError, - /// The layout of src is unknown - SrcLayoutUnknown, - /// The layout of dst is unknown - DstLayoutUnknown, - /// The size of src is overflow - SrcSizeOverflow, - /// The size of dst is overflow - DstSizeOverflow, -} - #[cfg(feature = "rustc")] mod rustc { use rustc_attr_ir::lang_items::LangItem; use rustc_middle::ty::consts::ConstExt; + use rustc_middle::ty::transmute::Answer; use rustc_middle::ty::{Const, Region, Ty, TyCtxt}; use super::*; @@ -122,7 +51,7 @@ mod rustc { src: Ty<'tcx>, dst: Ty<'tcx>, assume: crate::Assume, - ) -> crate::Answer, Ty<'tcx>> { + ) -> Answer, Ty<'tcx>> { crate::maybe_transmutable::MaybeTransmutableQuery::new(src, dst, assume, self.tcx) .answer() } diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index e9351d4081b6e..be4b1a882d966 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -4,9 +4,11 @@ pub(crate) mod query_context; #[cfg(test)] mod tests; +use rustc_middle::ty::transmute::{Answer, Condition, Reason}; + +use crate::Map; use crate::layout::{self, Def, Dfa, Reference, Tree, dfa, union}; use crate::maybe_transmutable::query_context::QueryContext; -use crate::{Answer, Condition, Map, Reason}; pub(crate) struct MaybeTransmutableQuery where @@ -299,67 +301,6 @@ where } } -impl Answer { - /// Requires both answers, combining their conditions into a conjunction. - fn and(self, rhs: Answer) -> Answer { - let lhs = self; - match (lhs, rhs) { - // Prefer a specific reason over generic bit incompatibility; - // otherwise, retain the left-hand reason. - (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason)) - | (Answer::No(reason), Answer::No(_)) - // If either is an error, return it - | (Answer::No(reason), _) | (_, Answer::No(reason)) => Answer::No(reason), - // If only one side has a condition, pass it along - (Answer::Yes, other) | (other, Answer::Yes) => other, - // If both sides have IfAll conditions, merge them - (Answer::If(Condition::IfAll(mut lhs)), Answer::If(Condition::IfAll(ref mut rhs))) => { - lhs.append(rhs); - Answer::If(Condition::IfAll(lhs)) - } - // If only one side is an IfAll, add the other Condition to it - (Answer::If(cond), Answer::If(Condition::IfAll(mut conds))) - | (Answer::If(Condition::IfAll(mut conds)), Answer::If(cond)) => { - conds.push(cond); - Answer::If(Condition::IfAll(conds)) - } - // Otherwise, both lhs and rhs conditions can be combined in a parent IfAll - (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAll(vec![lhs, rhs])), - } - } - - /// Combines alternative answers and collects their conditions in an `IfAny`. - /// - /// Currently, combining `Yes` with `If` retains the condition. This differs - /// from Boolean disjunction, where unconditional success would suffice. - fn or(self, rhs: Answer) -> Answer { - let lhs = self; - match (lhs, rhs) { - // Prefer a specific reason over generic bit incompatibility; - // otherwise, retain the left-hand reason. - (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason)) - | (Answer::No(reason), Answer::No(_)) => Answer::No(reason), - // Otherwise, errors can be ignored for the rest of the pattern matching - (Answer::No(_), other) | (other, Answer::No(_)) => other.or(Answer::Yes), - // If only one side has a condition, pass it along - (Answer::Yes, other) | (other, Answer::Yes) => other, - // If both sides have IfAny conditions, merge them - (Answer::If(Condition::IfAny(mut lhs)), Answer::If(Condition::IfAny(ref mut rhs))) => { - lhs.append(rhs); - Answer::If(Condition::IfAny(lhs)) - } - // If only one side is an IfAny, add the other Condition to it - (Answer::If(cond), Answer::If(Condition::IfAny(mut conds))) - | (Answer::If(Condition::IfAny(mut conds)), Answer::If(cond)) => { - conds.push(cond); - Answer::If(Condition::IfAny(conds)) - } - // Otherwise, both lhs and rhs conditions can be combined in a parent IfAny - (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAny(vec![lhs, rhs])), - } - } -} - enum Quantifier { ThereExists, ForAll, diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs index 371a362c09ff3..56d31cb83f57f 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -1,9 +1,10 @@ extern crate test; use itertools::Itertools; +use rustc_middle::ty::transmute::{Answer, Condition, Reason}; use super::query_context::test::{Def, UltraMinimal}; -use crate::{Answer, Assume, Condition, Reason, layout}; +use crate::{Assume, layout}; type Tree = layout::Tree; type Dfa = layout::Dfa; @@ -36,21 +37,18 @@ impl Representation for Dfa { } } -fn is_transmutable( - src: &R, - dst: &R, - assume: Assume, -) -> crate::Answer { +fn is_transmutable(src: &R, dst: &R, assume: Assume) -> Answer { let src = src.clone(); let dst = dst.clone(); R::is_transmutable(src, dst, assume) } mod answers { + use rustc_middle::ty::transmute::{Condition, Reason}; + use crate::maybe_transmutable::Quantifier; - use crate::{Condition, Reason}; - type Answer = crate::Answer; + type Answer = rustc_middle::ty::transmute::Answer; const OUTLIVES: Condition = Condition::Outlives { long: 1, short: 0 }; const IMMUTABLE: Condition = Condition::Immutable { ty: () }; @@ -141,11 +139,11 @@ mod answers { } mod safety { + use rustc_middle::ty::transmute::{Answer, Reason}; + use super::*; - use crate::Answer; - const DST_HAS_SAFETY_INVARIANTS: Answer = - Answer::No(crate::Reason::DstMayHaveSafetyInvariants); + const DST_HAS_SAFETY_INVARIANTS: Answer = Answer::No(Reason::DstMayHaveSafetyInvariants); #[test] fn src_safe_dst_safe() { @@ -354,7 +352,6 @@ mod uninit { mod alt { use super::*; - use crate::Answer; #[test] fn should_permit_identity_transmutation() { @@ -445,7 +442,6 @@ mod char { mod nonzero { use super::*; - use crate::{Answer, Reason}; const NONZERO_BYTE_WIDTHS: [u64; 5] = [1, 2, 4, 8, 16]; diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 0f7771cb78b0b..5c72f6cba3989 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -10,7 +10,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ FnAbiError, HasTyCtxt, HasTypingEnv, LayoutCx, LayoutOf, TyAndLayout, fn_can_unwind, }; -use rustc_middle::ty::{self, InstanceKind, ShimKind, Ty, TyCtxt, Unnormalized}; +use rustc_middle::ty::{self, Instance, InstanceKind, ShimKind, Ty, TyCtxt, Unnormalized}; use rustc_span::def_id::DefId; use rustc_span::{DUMMY_SP, bug}; use rustc_target::callconv::{ @@ -231,8 +231,7 @@ struct FnAbiDesc<'tcx> { layout_cx: LayoutCx<'tcx>, sig: ty::FnSig<'tcx>, - /// The function's definition, if its body can be used to deduce parameter attributes. - determined_fn_def_id: Option, + instance: Option>, caller_location: Option>, is_virtual_call: bool, extra_args: &'tcx [Ty<'tcx>], @@ -250,9 +249,7 @@ impl<'tcx> FnAbiDesc<'tcx> { typing_env, Unnormalized::new_wip(tcx.instantiate_bound_regions_with_erased(sig)), ), - // Parameter attributes can never be deduced for indirect calls, as there is no - // function body available to use. - determined_fn_def_id: None, + instance: None, caller_location: None, is_virtual_call: false, extra_args, @@ -265,19 +262,13 @@ impl<'tcx> FnAbiDesc<'tcx> { ) -> Self { let ty::PseudoCanonicalInput { typing_env, value: (instance, extra_args) } = query; let is_virtual_call = matches!(instance.def, ty::InstanceKind::Virtual(..)); - let is_tls_shim_call = - matches!(instance.def, ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(_))); Self { layout_cx: LayoutCx::new(tcx, typing_env), sig: tcx.normalize_erasing_regions( typing_env, Unnormalized::new_wip(fn_sig_for_fn_abi(tcx, instance, typing_env)), ), - // Parameter attributes can be deduced from the bodies of neither: - // - virtual calls, as they might call other functions from the vtable; nor - // - TLS shims, as they would refer to the underlying static. - determined_fn_def_id: (!is_virtual_call && !is_tls_shim_call) - .then(|| instance.def_id()), + instance: Some(instance), caller_location: instance .def .requires_caller_location(tcx) @@ -312,11 +303,14 @@ fn fn_abi_of_instance_raw<'tcx>( // delegate to it here in order to reuse (and, if necessary, augment) its result. tcx.fn_abi_of_instance_no_deduced_attrs(query).map(|fn_abi| { let params = FnAbiDesc::for_instance(tcx, query); - // If the function's body can be used to deduce parameter attributes, then adjust such - // "no deduced attrs" ABI; otherwise, return that ABI unadjusted. - params.determined_fn_def_id.map_or(fn_abi, |fn_def_id| { + // Apply attributes deduced from MIR body. This requires that an instance MIR is its optimized MIR. + // For example, DropGlue(def_id, ty) MIR is compiler generated, while optimized MIR for def_id is + // that of an empty placeholder function `drop_glue` from the standard library. + if let Some(Instance { def: InstanceKind::Item(fn_def_id), .. }) = params.instance { fn_abi_adjust_for_deduced_attrs(¶ms.layout_cx, fn_abi, params.sig.abi(), fn_def_id) - }) + } else { + fn_abi + } }) } @@ -327,7 +321,7 @@ fn arg_attrs_for_rust_scalar<'tcx>( layout: TyAndLayout<'tcx>, offset: Size, is_return: bool, - determined_fn_def_id: Option, + instance: Option>, ) -> ArgAttributes { let mut attrs = ArgAttributes::new(); @@ -381,13 +375,11 @@ fn arg_attrs_for_rust_scalar<'tcx>( && matches!(kind, PointerKind::MutableRef { unpin: true }) && !is_return { - let rustc_no_writable = match determined_fn_def_id { - Some(def_id) => find_attr!(tcx, def_id, RustcNoWritable), - None => true, // If no def_id exists, we make the conservative choice and disable the feature. - }; - - if !rustc_no_writable { - attrs.set(ArgAttribute::Writable); + if let Some(Instance { def: InstanceKind::Item(def_id), .. }) = instance { + let rustc_no_writable = find_attr!(tcx, def_id, RustcNoWritable); + if !rustc_no_writable { + attrs.set(ArgAttribute::Writable); + } } } @@ -536,15 +528,12 @@ fn fn_abi_sanity_check<'tcx>( fn_arg_sanity_check(cx, spec_abi, &fn_abi.ret, true); } -#[tracing::instrument( - level = "debug", - skip(cx, caller_location, determined_fn_def_id, is_virtual_call) -)] +#[tracing::instrument(level = "debug", skip(cx, caller_location, instance, is_virtual_call))] fn fn_abi_new_uncached<'tcx>( FnAbiDesc { layout_cx: ref cx, sig, - determined_fn_def_id, + instance, caller_location, is_virtual_call, extra_args, @@ -591,7 +580,7 @@ fn fn_abi_new_uncached<'tcx>( }; Ok(ArgAbi::new(layout, |scalar, offset| { - arg_attrs_for_rust_scalar(*cx, scalar, layout, offset, is_return, determined_fn_def_id) + arg_attrs_for_rust_scalar(*cx, scalar, layout, offset, is_return, instance) })) }; @@ -611,8 +600,17 @@ fn fn_abi_new_uncached<'tcx>( // FIXME return false for tls shim can_unwind: fn_can_unwind( tcx, - // Since `#[rustc_nounwind]` can change unwinding, we cannot infer unwinding by `fn_def_id` for a virtual call. - determined_fn_def_id, + instance.and_then(|instance| match instance.def { + InstanceKind::Item(item) => Some(item), + InstanceKind::Intrinsic(item) => Some(item), + // Used to recognize that with -Z panic-in-drop=abort, `drop_glue` never unwinds. + InstanceKind::Shim(ShimKind::DropGlue(def_id, _ty)) => Some(def_id), + InstanceKind::Shim(_) => None, + // Don't apply rustc_nounwind from trait method to impls. + InstanceKind::Virtual(..) => None, + // Not used with fn_abi_of_instance. + InstanceKind::LlvmIntrinsic(_) => None, + }), sig.abi(), ), }; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 0bc901e07ea96..246d6241ec8ba 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -46,6 +46,7 @@ pub enum SolverTraitLangItem { FnMut, FnOnce, FnPtrTrait, + Freeze, FusedIterator, Future, Iterator, diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 0be6a2c4152dc..eab69fa224d92 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -61,6 +61,7 @@ pub mod relate; pub mod search_graph; pub mod solve; pub mod sty; +pub mod transmute; pub mod walk; // These modules are not `pub` since they are glob-imported. diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 4a6e704cb0020..6f7a1ede0315d 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -54,9 +54,9 @@ use crate::inherent::*; use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo}; use crate::visit::TypeVisitableExt; use crate::{ - AliasTy, Binder, BoundRegion, BoundVar, BoundVariableKind, ClauseKind, DebruijnIndex, - InferCtxtLike, Interner, IsRigid, OutlivesClause, Region, RegionKind, TyKind, TypeFoldable, - TypeFolder, TypingMode, UniverseIndex, Variance, elaborate, max_universe, + AliasTy, Binder, BoundRegion, BoundRegionKind, BoundVar, BoundVariableKind, ClauseKind, + DebruijnIndex, InferCtxtLike, Interner, IsRigid, OutlivesClause, Region, RegionKind, TyKind, + TypeFoldable, TypeFolder, TypingMode, UniverseIndex, Variance, elaborate, max_universe, set_aliases_to_non_rigid, }; @@ -964,16 +964,16 @@ fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handl let higher_ranked_candidate_for_outlived_region = |region| { let bound_outlives = bound_outlives.map_bound(|(alias, _)| (alias, region)); - let mut replacer = PlaceholderReplacer { - cx: infcx.cx(), - existing_var_count: bound_outlives.bound_vars().len(), - bound_vars: IndexMap::default(), + let mut replacer = MakeHigherRankederer { + cx: infcx, + preexisting_var_count: bound_outlives.bound_vars().len(), + regions: IndexMap::default(), universe: u, current_index: DebruijnIndex::ZERO, }; let escaping_outlives = bound_outlives.skip_binder().fold_with(&mut replacer); let bound_vars = bound_outlives.bound_vars().iter().chain( - core::mem::take(&mut replacer.bound_vars) + core::mem::take(&mut replacer.regions) .into_iter() .map(|(_, bound_region)| BoundVariableKind::Region(bound_region.kind)), ); @@ -982,7 +982,7 @@ fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handl I::BoundVarKinds::from_vars(infcx.cx(), bound_vars), ); - // `PlaceholderReplacer` only folds regions. A non-lifetime binder can leave + // `MakeHigherRankederer` only folds regions. A non-lifetime binder can leave // a placeholder type in `u`, so this type-outlives constraint cannot be // handled by the region-outlives-only eager placeholder machinery. if max_universe(infcx, bound_outlives) == u { @@ -1078,30 +1078,52 @@ pub fn regions_outlived_by_placeholder( }) } -pub struct PlaceholderReplacer { - cx: I, - existing_var_count: usize, - bound_vars: IndexMap>, +pub struct MakeHigherRankederer<'a, I: Interner, Infcx: InferCtxtLike> { + cx: &'a Infcx, + preexisting_var_count: usize, + regions: IndexMap, BoundRegion>, universe: UniverseIndex, current_index: DebruijnIndex, } -impl TypeFolder for PlaceholderReplacer { +impl<'a, I: Interner, Infcx: InferCtxtLike> MakeHigherRankederer<'a, I, Infcx> { + fn existing_var_count(&self) -> usize { + self.preexisting_var_count + self.regions.len() + } +} + +impl<'a, I: Interner, Infcx: InferCtxtLike> TypeFolder + for MakeHigherRankederer<'a, I, Infcx> +{ fn cx(&self) -> I { - self.cx + self.cx.cx() } fn fold_region(&mut self, r: Region) -> Region { + let cx = self.cx(); + match r.kind() { RegionKind::RePlaceholder(p) if p.universe == self.universe => { - let bound_vars_len = self.bound_vars.len(); - let mapped_var = self.bound_vars.entry(p.bound.var).or_insert(BoundRegion { - var: BoundVar::from_usize(self.existing_var_count + bound_vars_len), + let existing_var_count = self.existing_var_count(); + let mapped_var = self.regions.entry(r).or_insert(BoundRegion { + var: BoundVar::from_usize(existing_var_count), kind: p.bound.kind, }); - Region::new_bound(self.cx, self.current_index, *mapped_var) + Region::new_bound(cx, self.current_index, *mapped_var) + } + RegionKind::ReVar(inf) => { + let u = self.cx.universe_of_region(inf).unwrap(); + if u == self.universe { + let existing_var_count = self.existing_var_count(); + let mapped_var = self.regions.entry(r).or_insert(BoundRegion { + var: BoundVar::from_usize(existing_var_count), + kind: BoundRegionKind::Anon, + }); + Region::new_bound(cx, self.current_index, *mapped_var) + } else { + r + } } - // FIXME(-Zassumptions-on-binders): We should be handling region variables here somehow _ => r, } } diff --git a/compiler/rustc_type_ir/src/transmute.rs b/compiler/rustc_type_ir/src/transmute.rs new file mode 100644 index 0000000000000..f74bd3b954d1a --- /dev/null +++ b/compiler/rustc_type_ir/src/transmute.rs @@ -0,0 +1,132 @@ +/// The result of a transmutability query under the supplied `Assume` options. +#[derive(Debug, Hash, Eq, PartialEq, Clone)] +pub enum Answer { + /// The analysis requires no further conditions. + Yes, + /// The analysis could not establish transmutability. + No(Reason), + /// Transmutability depends on conditions that the trait solver must discharge. + If(Condition), +} + +/// A condition which must hold for safe transmutation to be possible. +#[derive(Debug, Hash, Eq, PartialEq, Clone)] +pub enum Condition { + /// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`. + Transmutable { src: T, dst: T }, + + /// The region `long` must outlive `short`. + Outlives { long: R, short: R }, + + /// The type `ty` must satisfy `Freeze`. + Immutable { ty: T }, + + /// `Src` is transmutable into `Dst`, if all of the enclosed requirements are met. + IfAll(Vec>), + + /// `Src` is transmutable into `Dst` if any of the enclosed requirements are met. + IfAny(Vec>), +} + +/// Answers "why wasn't the source type transmutable into the destination type?" +#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)] +pub enum Reason { + /// The layout of the source type is not yet supported. + SrcIsNotYetSupported, + /// The layout of the destination type is not yet supported. + DstIsNotYetSupported, + /// The layout of the destination type is bit-incompatible with the source type. + DstIsBitIncompatible, + /// The destination type is uninhabited. + DstUninhabited, + /// The destination type may carry safety invariants. + DstMayHaveSafetyInvariants, + /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized. + DstIsTooBig, + /// The destination referent is larger than the source referent. + DstRefIsTooBig { + /// The referent of the source type. + src: T, + /// The size of the source type's referent. + src_size: usize, + /// The too-large referent of the destination type. + dst: T, + /// The size of the destination type's referent. + dst_size: usize, + }, + /// The destination referent requires stricter alignment than the source referent. + DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize }, + /// Can't go from shared pointer to unique pointer + DstIsMoreUnique, + /// Encountered a type error + TypeError, + /// The layout of src is unknown + SrcLayoutUnknown, + /// The layout of dst is unknown + DstLayoutUnknown, + /// The size of src is overflow + SrcSizeOverflow, + /// The size of dst is overflow + DstSizeOverflow, +} + +impl Answer { + /// Requires both answers, combining their conditions into a conjunction. + pub fn and(self, rhs: Answer) -> Answer { + let lhs = self; + match (lhs, rhs) { + // Prefer a specific reason over generic bit incompatibility; + // otherwise, retain the left-hand reason. + (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason)) + | (Answer::No(reason), Answer::No(_)) + // If either is an error, return it + | (Answer::No(reason), _) | (_, Answer::No(reason)) => Answer::No(reason), + // If only one side has a condition, pass it along + (Answer::Yes, other) | (other, Answer::Yes) => other, + // If both sides have IfAll conditions, merge them + (Answer::If(Condition::IfAll(mut lhs)), Answer::If(Condition::IfAll(ref mut rhs))) => { + lhs.append(rhs); + Answer::If(Condition::IfAll(lhs)) + } + // If only one side is an IfAll, add the other Condition to it + (Answer::If(cond), Answer::If(Condition::IfAll(mut conds))) + | (Answer::If(Condition::IfAll(mut conds)), Answer::If(cond)) => { + conds.push(cond); + Answer::If(Condition::IfAll(conds)) + } + // Otherwise, both lhs and rhs conditions can be combined in a parent IfAll + (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAll(vec![lhs, rhs])), + } + } + + /// Combines alternative answers and collects their conditions in an `IfAny`. + /// + /// Currently, combining `Yes` with `If` retains the condition. This differs + /// from Boolean disjunction, where unconditional success would suffice. + pub fn or(self, rhs: Answer) -> Answer { + let lhs = self; + match (lhs, rhs) { + // Prefer a specific reason over generic bit incompatibility; + // otherwise, retain the left-hand reason. + (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason)) + | (Answer::No(reason), Answer::No(_)) => Answer::No(reason), + // Otherwise, errors can be ignored for the rest of the pattern matching + (Answer::No(_), other) | (other, Answer::No(_)) => other.or(Answer::Yes), + // If only one side has a condition, pass it along + (Answer::Yes, other) | (other, Answer::Yes) => other, + // If both sides have IfAny conditions, merge them + (Answer::If(Condition::IfAny(mut lhs)), Answer::If(Condition::IfAny(ref mut rhs))) => { + lhs.append(rhs); + Answer::If(Condition::IfAny(lhs)) + } + // If only one side is an IfAny, add the other Condition to it + (Answer::If(cond), Answer::If(Condition::IfAny(mut conds))) + | (Answer::If(Condition::IfAny(mut conds)), Answer::If(cond)) => { + conds.push(cond); + Answer::If(Condition::IfAny(conds)) + } + // Otherwise, both lhs and rhs conditions can be combined in a parent IfAny + (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAny(vec![lhs, rhs])), + } + } +} diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 27951a65f6c84..be3dc5e013b32 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -2536,47 +2536,46 @@ pub fn addr_eq(p: *const T, q: *const U) -> bo /// This is the same as `f == g`, but using this function makes clear that the potentially /// surprising semantics of function pointer comparison are involved. /// -/// There are **very few guarantees** about how functions are compiled and they have no intrinsic +/// There are **basically no guarantees** about how functions are compiled as they have no intrinsic /// β€œidentity”; in particular, this comparison: /// -/// * May return `true` unexpectedly, in cases where functions are equivalent. +/// * May return `true` unexpectedly, even in cases where functions are not logically equivalent, +/// as long as they compile to the same machine code. /// /// For example, the following program is likely (but not guaranteed) to print `(true, true)` /// when compiled with optimization: /// /// ``` +/// use std::hint::assert_unchecked; +/// /// let f: fn(i32) -> i32 = |x| x; -/// let g: fn(i32) -> i32 = |x| x + 0; // different closure, different body -/// let h: fn(u32) -> u32 = |x| x + 0; // different signature too -/// dbg!(std::ptr::fn_addr_eq(f, g), std::ptr::fn_addr_eq(f, h)); // not guaranteed to be equal +/// let g: fn(u32) -> u32 = |x| x + 0; // different type, different body +/// let h: unsafe fn(i32) -> i32 = |x| unsafe { assert_unchecked(x != 0); x }; // different unsafe preconditions +/// // These three functions are allowed but not guaranteed to compare equal. +/// dbg!(std::ptr::fn_addr_eq(f, g), std::ptr::fn_addr_eq(f, h)); /// ``` /// +/// In other words, functions that compare equal can have different behavior! Even if +/// `fn_addr_eq(f, h)` is true, it is still undefined behavior to call `h(0)`, while `f(0)` is +/// safe to call. +/// +/// The explanation for this is that function pointers have [provenance][crate::ptr#provenance]. +/// If functions have the same assembly and get merged, their provenance decides which variant of +/// the function will be called. `f` and `h` therefore end up with the same address but different +/// provenance, and `fn_addr_eq` only compares the address. +/// /// * May return `false` in any case. /// /// This is particularly likely with generic functions but may happen with any function. /// (From an implementation perspective, this is possible because functions may sometimes be /// processed more than once by the compiler, resulting in duplicate machine code.) /// -/// Despite these false positives and false negatives, this comparison can still be useful. -/// Specifically, if -/// -/// * `T` is the same type as `U`, `T` is a [subtype] of `U`, or `U` is a [subtype] of `T`, and -/// * `ptr::fn_addr_eq(f, g)` returns true, -/// -/// then calling `f` and calling `g` will be equivalent. -/// -/// -/// # Examples -/// -/// ``` -/// use std::ptr; -/// -/// fn a() { println!("a"); } -/// fn b() { println!("b"); } -/// assert!(!ptr::fn_addr_eq(a as fn(), b as fn())); -/// ``` -/// -/// [subtype]: https://doc.rust-lang.org/reference/subtyping.html +/// These limitations imply that comparing function pointers (via this function or via `==`) is only +/// useful in extremely niche circumstances. For instance, if a library maintains a set of +/// callbacks, and doesn't guarantee which of them are actually called (i.e., callbacks can be +/// spuriously skipped), then it can be legitimate to deduplicate callbacks based on `fn_addr_eq`. +/// This can never introduce undefined behavior because it only skips executing some of the +/// registered callbacks. #[stable(feature = "ptr_fn_addr_eq", since = "1.85.0")] #[inline(always)] #[must_use = "function pointer comparison produces a value"] diff --git a/src/etc/debugger_tester/common.py b/src/etc/debugger_tester/common.py index cf6fe3b3b63e5..4e33dbafe5e20 100644 --- a/src/etc/debugger_tester/common.py +++ b/src/etc/debugger_tester/common.py @@ -551,6 +551,13 @@ def save_blessing(self, metadata: BlessMetadata): self.bless_metadata = metadata path = os.environ["DEBUGGER_TESTER_INPUT_DATA_PATH"] + + # We first sort the types dictionary. This keeps the git diff more consistent, and doesn't + # affect runtime behavior at all. + import operator + + self.types = dict(sorted(self.types.items(), key=operator.itemgetter(0))) + # dumping directly to a file is somewhat unsafe. If the `Variable`/`Type` data ends up in a # state that cannot be serialized correctly, the json ends up malformed, and we could end up # overwriting valid test data with a complete mess. Since the in-memory data is typically @@ -567,7 +574,7 @@ def save_blessing(self, metadata: BlessMetadata): os.makedirs(pathlib.Path(path).parent, exist_ok=True) - with open(path, "w") as f: + with open(path, "w", newline="\n") as f: f.write(x) f.write("\n") diff --git a/src/etc/debugger_tester/lldb/from_lldb.py b/src/etc/debugger_tester/lldb/from_lldb.py index 6baee69aa99ae..1fcf78b7930cd 100644 --- a/src/etc/debugger_tester/lldb/from_lldb.py +++ b/src/etc/debugger_tester/lldb/from_lldb.py @@ -127,7 +127,7 @@ def type_unpack_fmt(kind: int, size: int) -> str: fmt = _SIZE_TO_INT_FMT[size] if kind in _UNSIGNED_INT_TYPES: - fmt.upper() + fmt = fmt.upper() return fmt diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 35b2af5370fff..bc6bfc27b7ba0 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -575,7 +575,7 @@ pub(crate) struct RunnableDocTest<'a> { /// In a standalone test this is just the regular test code. full_test_code: String, full_test_line_offset: usize, - test_opts: &'a IndividualTestOptions, + test_opts: IndividualTestOptions, global_opts: &'a GlobalTestOptions, langstr: LangString, line: usize, @@ -1143,8 +1143,6 @@ fn generate_test_desc_and_fn( unused_externs: Arc>>, ) -> test::TestDescAndFn { let target_str = rustdoc_options.target.to_string(); - let rustdoc_test_options = - IndividualTestOptions::new(&rustdoc_options, &test.test_id, scraped_test.local_path()); debug!("creating test {}: {}", scraped_test.name, scraped_test.text); test::TestDescAndFn { @@ -1168,8 +1166,13 @@ fn generate_test_desc_and_fn( test_type: test::TestType::DocTest, }, testfn: test::DynTestFn(Arc::new(move || { + let rustdoc_test_options = IndividualTestOptions::new( + &rustdoc_options, + &test.test_id, + scraped_test.local_path(), + ); doctest_run_fn( - &rustdoc_test_options, + rustdoc_test_options, &opts, &test, &scraped_test, @@ -1181,7 +1184,7 @@ fn generate_test_desc_and_fn( } fn doctest_run_fn( - test_opts: &IndividualTestOptions, + test_opts: IndividualTestOptions, global_opts: &GlobalTestOptions, doctest: &DocTestBuilder, scraped_test: &ScrapedDocTest, diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 3e4b4209f4145..d1d149b72ac4e 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -198,7 +198,7 @@ test::test_main(test_args, tests) let runnable_test = RunnableDocTest { full_test_code: format!("{code_prefix}{code}", code = self.output), full_test_line_offset: 0, - test_opts: &test_options, + test_opts: test_options, global_opts: opts, langstr: LangString::default(), line: 0, diff --git a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml index 0f510ecf4723d..13b5b0be73420 100644 --- a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml +++ b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml @@ -14,6 +14,10 @@ jobs: if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event_name == 'workflow_dispatch' }} name: publish runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + environment: publish-auto steps: - name: Checkout repository uses: actions/checkout@v6 @@ -30,9 +34,12 @@ jobs: - name: Install cargo-workspaces run: cargo install --locked cargo-workspaces --version "0.3.6" + - uses: rust-lang/crates-io-auth-action@v1 + id: auth + - name: Publish Crates env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} RUN_NUMBER: ${{ github.run_number }} shell: bash run: | diff --git a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml index ff04f1b830666..47e8f9e7f86ec 100644 --- a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml +++ b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml @@ -12,6 +12,10 @@ jobs: if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event_name == 'workflow_dispatch' }} name: publish runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + environment: publish-libs steps: - name: Checkout repository uses: actions/checkout@v6 @@ -24,9 +28,12 @@ jobs: - name: Install cargo-workspaces run: cargo install --locked cargo-workspaces --version "0.3.6" + - uses: rust-lang/crates-io-auth-action@v1 + id: auth + - name: Publish Crates env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} shell: bash run: | git config --global user.email "runner@gha.local" diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index 85ff3d08a78a3..ac5901b734b58 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -236,7 +236,7 @@ jobs: - run: ls -al ./dist - name: Publish Release - uses: ./.github/actions/github-release + uses: $/.github/actions/github-release with: files: "dist/*" name: ${{ env.TAG }} diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index cf4ba4404b367..4522be266f479 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -873,6 +873,7 @@ dependencies = [ "tracing", "triomphe", "tt", + "unicode-ident", ] [[package]] @@ -2446,9 +2447,9 @@ dependencies = [ [[package]] name = "salsa" -version = "0.28.2" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf0e374215cd2db2b5c75d7b3a99cb0cc052c0595335dfdefc03d4eb08f4aa81" +checksum = "27d2e6e19e39591cd99c01ac4141544b4d012cfd0831348629b8187fa9fe6d34" dependencies = [ "boxcar", "crossbeam-queue", @@ -2473,15 +2474,15 @@ dependencies = [ [[package]] name = "salsa-macro-rules" -version = "0.28.2" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85f4b7d4405540bbd6d4ffa52d4322d983f3781954d3073067ac1bdb028459b3" +checksum = "baf90a551c38f25ed8489111d3d5827aa18e1b85963f5e6a298505b1d1e2cb7e" [[package]] name = "salsa-macros" -version = "0.28.2" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445be2bfbb2f67cb663225ecd7bc5a25370c0250fca30f9d8cbad9a913650370" +checksum = "69dd77f86ec10e72e314f4ddf7c44c8d349e73cdff1aad524ba9668603fb0549" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 54e82148665b6..ac8887b3303ce 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -122,7 +122,7 @@ rayon = "1.10.0" rowan = "0.17.0" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it -salsa = { version = "0.28.2", default-features = false, features = [ +salsa = { version = "0.28.5", default-features = false, features = [ "rayon", "salsa_unstable", "macros", @@ -165,6 +165,10 @@ hashbrown = { version = "0.17.0", features = [ # We need to freeze the version of the crate, as the raw-api feature is considered unstable dashmap = { version = "=6.2.1", features = ["raw-api", "inline"] } +# We need to freeze this as `1.0.26` breaks the compilation of `rustc_lexer` during our in-tree +# libraries publish +unicode-ident = "=1.0.24" + [workspace.lints.cargo] unused_dependencies = "allow" diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index eb1e774b8f41b..eb2d47b7b8261 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -9,6 +9,9 @@ edition.workspace = true license.workspace = true rust-version.workspace = true +[package.metadata.cargo-machete] +ignored = ["unicode-ident"] + [lib] doctest = false @@ -44,6 +47,10 @@ span.workspace = true thin-vec.workspace = true syntax-bridge.workspace = true +# This is a transitive dep, but we need to specify this +# for the workspace-wide pinning +unicode-ident.workspace = true + [dev-dependencies] expect-test.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs index 583bde92679dd..801a4e6ecc1ce 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs @@ -7,6 +7,7 @@ //! and highlight injection). use std::{ + borrow::Cow, convert::Infallible, ops::{ControlFlow, Range}, }; @@ -222,9 +223,12 @@ impl Docs { fn extend_with_doc_attr(&mut self, value: ast::String, indent: &mut Indent) { let Some(value_offset) = value.text_range_between_quotes() else { return }; let value_offset = value_offset.start(); - let Ok(value) = value.value() else { return }; - // FIXME: Handle source maps for escaped text. - self.extend_with_doc_str(&value, value_offset, DocCommentKind::Desugared, indent); + let Ok(unescaped) = value.value() else { return }; + // `value()` borrows the source text only when the string has no escapes. With escapes, + // offsets in the unescaped text don't match offsets in the source, so leave it unmapped. + // FIXME: Map the text between escapes. + let ast_offset = matches!(unescaped, Cow::Borrowed(_)).then_some(value_offset); + self.push_doc_lines(&unescaped, ast_offset, DocCommentKind::Desugared, indent); } fn extend_with_doc_str( @@ -791,7 +795,7 @@ pub(crate) fn extract_docs<'a, 'db>( mod tests { use expect_test::expect; use hir_expand::InFile; - use syntax::{AstNode, ast}; + use syntax::{AstNode, AstToken, ast}; use test_fixture::WithFixture; use thin_vec::ThinVec; use tt::{TextRange, TextSize}; @@ -1079,4 +1083,54 @@ mod tests { assert_eq!(mapped(0, 3), Some((in_file(range(7, 10)), IsInnerDoc::No))); assert_eq!(mapped(4, 7), Some((in_file(range(14, 17)), IsInnerDoc::No))); } + + /// Extracts the docs of the first string in `source`, as if it were a `#[doc = "..."]` value. + fn doc_attr_docs(source: &str) -> Docs { + let (_db, file_id) = TestDB::with_single_file(""); + let value = syntax::SourceFile::parse(source, span::Edition::CURRENT) + .syntax_node() + .descendants_with_tokens() + .filter_map(|it| it.into_token()) + .find_map(ast::String::cast) + .expect("no string in the fixture"); + let mut docs = Docs { + docs: String::new(), + docs_source_map: Vec::new(), + outline_mod: None, + inline_file: file_id.into(), + prefix_len: TextSize::new(0), + inline_inner_docs_start: None, + outline_inner_docs_start: None, + macro_calls: ThinVec::new(), + }; + let mut indent = Indent::default(); + docs.extend_with_doc_attr(value, &mut indent); + docs.remove_indent(&indent); + docs.remove_last_newline(); + docs + } + + #[test] + fn doc_attr_source_map() { + let range = |start, end| TextRange::new(TextSize::new(start), TextSize::new(end)); + let mapped = |docs: &Docs, start, end| { + docs.find_ast_range(range(start, end)).map(|(range, _)| range.value) + }; + + // No escapes: `bar` maps back to the source. + let docs = doc_attr_docs(r#"#[doc = "foo bar"] fn f() {}"#); + assert_eq!(docs.docs, "foo bar"); + assert_eq!(mapped(&docs, 4, 7), Some(range(13, 16))); + + // Raw strings have no escapes either. + let docs = doc_attr_docs(r##"#[doc = r"a\b"] fn f() {}"##); + assert_eq!(docs.docs, r"a\b"); + assert_eq!(mapped(&docs, 2, 3), Some(range(12, 13))); + + // With an escape, `b` is at offset 2 in the docs but at offset 3 in the source, + // so the docs can't be mapped back. + let docs = doc_attr_docs(r#"#[doc = "a\"b"] fn f() {}"#); + assert_eq!(docs.docs, "a\"b"); + assert_eq!(mapped(&docs, 2, 3), None); + } } 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 b5d5145927702..5bd865ec6cc62 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 @@ -1925,7 +1925,11 @@ impl<'db> ExprCollector<'db> { self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr) } ast::Expr::FormatArgsExpr(f) => self.collect_format_args(f, syntax_ptr), - ast::Expr::IncludeBytesExpr(_) => self.alloc_expr(Expr::IncludeBytes, syntax_ptr) + ast::Expr::IncludeBytesExpr(_) => self.alloc_expr(Expr::IncludeBytes, syntax_ptr), + ast::Expr::CfgPredExpr(e) => { + let enabled = Expr::Literal(Literal::Bool(self.check_cfg_pred(e.cfg_predicate()))); + self.alloc_expr(enabled, syntax_ptr) + }, }) } @@ -3083,6 +3087,11 @@ impl<'db> ExprCollector<'db> { } ast::Pat::NotNull(_) => Pat::NotNull, ast::Pat::ConstBlockPat(_) => Pat::Missing, + ast::Pat::CfgPredPat(p) => { + let enabled = Expr::Literal(Literal::Bool(self.check_cfg_pred(p.cfg_predicate()))); + let expr_id = self.alloc_expr_from_pat(enabled, AstPtr::new(&pat)); + Pat::Lit(expr_id) + } ast::Pat::MacroPat(mac) => { return self.collect_macro_pat_with(mac.clone(), |this, expanded_pat| { this.collect_pat(expanded_pat, binding_list) @@ -3327,6 +3336,12 @@ impl<'db> ExprCollector<'db> { } } + /// Used for `cfg!()`, conducting imprecise checks + fn check_cfg_pred(&self, pred: Option) -> bool { + let expr = pred.map_or(cfg::CfgExpr::Invalid, cfg::CfgExpr::parse_from_ast); + self.cfg_options.check(&expr) != Some(false) + } + fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) { self.store.binding_definitions.entry(binding_id).or_default().push(pat_id); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs index 65877fb627f24..2e92aea417d23 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs @@ -86,10 +86,13 @@ impl GenericParamsCollector { pub(crate) fn finish(self) -> GenericParams { let Self { mut lifetimes, mut type_or_consts, where_predicates, parent: _ } = self; + let early_bound_lifetimes_len = + lifetimes.iter().filter(|(_, lt)| lt.is_early_bound()).count(); lifetimes.shrink_to_fit(); type_or_consts.shrink_to_fit(); GenericParams { + early_bound_lifetimes_len, type_or_consts, lifetimes, where_predicates: where_predicates.into_boxed_slice(), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs index 2e0b6f1ae1cde..9e1053baa0bc4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs @@ -44,9 +44,15 @@ pub enum LifetimeBoundType { } impl LifetimeParamData { + #[inline] pub fn is_late_bound(&self) -> bool { self.bound_type == LifetimeBoundType::LateBound } + + #[inline] + pub fn is_early_bound(&self) -> bool { + self.bound_type == LifetimeBoundType::EarlyBound + } } /// Data about a generic const parameter (to a function, struct, impl, ...). @@ -164,6 +170,7 @@ pub struct GenericParams { pub(crate) type_or_consts: Arena, pub(crate) lifetimes: Arena, pub(crate) where_predicates: Box<[WherePredicate]>, + pub(crate) early_bound_lifetimes_len: usize, } impl ops::Index for GenericParams { @@ -194,6 +201,7 @@ static EMPTY: LazyLock = LazyLock::new(|| GenericParams { type_or_consts: Arena::default(), lifetimes: Arena::default(), where_predicates: Box::default(), + early_bound_lifetimes_len: 0, }); impl GenericParams { @@ -304,14 +312,24 @@ impl GenericParams { self.type_or_consts.len() + self.lifetimes.len() } + #[inline] + pub fn len_no_late(&self) -> usize { + self.type_or_consts.len() + self.early_bound_lifetimes_len + } + #[inline] pub fn len_lifetimes(&self) -> usize { - self.lifetimes.len() - self.len_late_bound_lifetimes() + self.lifetimes.len() + } + + #[inline] + pub fn len_early_bound_lifetimes(&self) -> usize { + self.early_bound_lifetimes_len } #[inline] pub fn len_late_bound_lifetimes(&self) -> usize { - self.lifetimes.iter().filter(|(_, p)| p.bound_type == LifetimeBoundType::LateBound).count() + self.len_lifetimes() - self.len_early_bound_lifetimes() } #[inline] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs index e290601d3cb16..0a2d0bf12e7d4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -35,9 +35,9 @@ macro_rules! f { }; } -struct#0:MacroRules[BE8F, 0]@58..64#1920# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#1920# - map#0:MacroRules[BE8F, 0]@86..89#1920#:#0:MacroRules[BE8F, 0]@89..90#1920# #0:MacroRules[BE8F, 0]@89..90#1920#::#0:MacroRules[BE8F, 0]@91..93#1920#std#0:MacroRules[BE8F, 0]@93..96#1920#::#0:MacroRules[BE8F, 0]@96..98#1920#collections#0:MacroRules[BE8F, 0]@98..109#1920#::#0:MacroRules[BE8F, 0]@109..111#1920#HashSet#0:MacroRules[BE8F, 0]@111..118#1920#<#0:MacroRules[BE8F, 0]@118..119#1920#(#0:MacroRules[BE8F, 0]@119..120#1920#)#0:MacroRules[BE8F, 0]@120..121#1920#>#0:MacroRules[BE8F, 0]@121..122#1920#,#0:MacroRules[BE8F, 0]@122..123#1920# -}#0:MacroRules[BE8F, 0]@132..133#1920# +struct#0:MacroRules[BE8F, 0]@58..64#1922# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#1922# + map#0:MacroRules[BE8F, 0]@86..89#1922#:#0:MacroRules[BE8F, 0]@89..90#1922# #0:MacroRules[BE8F, 0]@89..90#1922#::#0:MacroRules[BE8F, 0]@91..93#1922#std#0:MacroRules[BE8F, 0]@93..96#1922#::#0:MacroRules[BE8F, 0]@96..98#1922#collections#0:MacroRules[BE8F, 0]@98..109#1922#::#0:MacroRules[BE8F, 0]@109..111#1922#HashSet#0:MacroRules[BE8F, 0]@111..118#1922#<#0:MacroRules[BE8F, 0]@118..119#1922#(#0:MacroRules[BE8F, 0]@119..120#1922#)#0:MacroRules[BE8F, 0]@120..121#1922#>#0:MacroRules[BE8F, 0]@121..122#1922#,#0:MacroRules[BE8F, 0]@122..123#1922# +}#0:MacroRules[BE8F, 0]@132..133#1922# "#]], ); } @@ -197,7 +197,7 @@ macro_rules! mk_struct { #[macro_use] mod foo; -struct#1:MacroRules[DB0C, 0]@59..65#1920# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#1920#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#1920#;#1:MacroRules[DB0C, 0]@75..76#1920# +struct#1:MacroRules[DB0C, 0]@59..65#1922# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#1922#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#1922#;#1:MacroRules[DB0C, 0]@75..76#1922# "#]], ); } @@ -423,10 +423,10 @@ m! { foo, bar } macro_rules! m { ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } ); } -impl#\1920# Bar#\1920# {#\1920# - fn#\1920# foo#\ROOT2024#(#\1920#)#\1920# {#\1920#}#\1920# - fn#\1920# bar#\ROOT2024#(#\1920#)#\1920# {#\1920#}#\1920# -}#\1920# +impl#\1922# Bar#\1922# {#\1922# + fn#\1922# foo#\ROOT2024#(#\1922#)#\1922# {#\1922#}#\1922# + fn#\1922# bar#\ROOT2024#(#\1922#)#\1922# {#\1922#}#\1922# +}#\1922# "#]], ); } 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 44579304d3646..6dbf3675cdbc0 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 @@ -429,15 +429,17 @@ fn cfg_select_expand( } fn cfg_expand( - db: &dyn SourceDatabase, - id: MacroCallId, + _db: &dyn SourceDatabase, + _id: MacroCallId, tt: &tt::TopSubtree, span: Span, ) -> ExpandResult { - let loc = id.loc(db); - let expr = CfgExpr::parse(tt); - let enabled = loc.krate.cfg_options(db).check(&expr) != Some(false); - let expanded = if enabled { quote!(span=>true) } else { quote!(span=>false) }; + let pound = mk_pound(span); + // Support ide features, like completion and hover + // This implementation should not be used for 'cfg_select' because 'cfg_select' expands any token. + let expanded = quote! {span => + builtin #pound cfg #tt + }; ExpandResult::ok(expanded) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs index e471c6d2bd3e2..aab2889cdcf45 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs @@ -22,8 +22,6 @@ // FIXME: Move this into the span crate? Not quite possible today as that depends on `MacroCallLoc` // which contains a bunch of unrelated things -use std::convert::identity; - use span::{Edition, MacroCallId, Span, SyntaxContext}; use base_db::SourceDatabase; @@ -114,20 +112,17 @@ fn apply_mark_internal( transparency: Transparency, edition: Edition, ) -> SyntaxContext { - let call_id = Some(call_id); - - let mut opaque = ctxt.opaque(db); - let mut opaque_and_semiopaque = ctxt.opaque_and_semiopaque(db); + let (mut opaque, mut opaque_and_semiopaque) = ctxt.opaque_and_opaque_and_semiopaque(db); if transparency >= Transparency::Opaque { let parent = opaque; - opaque = SyntaxContext::new(db, call_id, transparency, edition, parent, identity, identity); + opaque = SyntaxContext::new(db, call_id, transparency, edition, parent, None, None); } if transparency >= Transparency::SemiOpaque { let parent = opaque_and_semiopaque; opaque_and_semiopaque = - SyntaxContext::new(db, call_id, transparency, edition, parent, |_| opaque, identity); + SyntaxContext::new(db, call_id, transparency, edition, parent, Some(opaque), None); } let parent = ctxt; @@ -137,7 +132,7 @@ fn apply_mark_internal( transparency, edition, parent, - |_| opaque, - |_| opaque_and_semiopaque, + Some(opaque), + Some(opaque_and_semiopaque), ) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index e646c21ec8c5e..6a3efe55bdcc5 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -537,11 +537,6 @@ impl MacroCallId { /// query, only typing in the macro call itself changes the returned /// subtree. #[salsa::tracked(returns(ref))] - #[allow( - useless_deprecated, - unused_attributes, - reason = "salsa bug, see https://github.com/salsa-rs/salsa/issues/1307" - )] #[deprecated = "calling this is incorrect, call `macro_arg_considering_derives` instead"] fn macro_arg(self, db: &dyn SourceDatabase) -> MacroArgResult { let loc = self.loc(db); 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 53a232adfc7ac..f17ddb60e0a78 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 @@ -179,12 +179,12 @@ pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPr simple_trait_predicates(interner, loc, generic_params, adt_predicates, trait_id) } BuiltinDeriveImplTrait::Reborrow => { - explicit_own_predicates(interner, adt_predicates.own_explicit_predicates()) + explicit_own_predicates(adt_predicates.own_explicit_predicates()) } BuiltinDeriveImplTrait::Default => { if matches!(loc.adt, AdtId::EnumId(_)) { // Enums don't have extra bounds. - explicit_own_predicates(interner, adt_predicates.own_explicit_predicates()) + explicit_own_predicates(adt_predicates.own_explicit_predicates()) } else { simple_trait_predicates(interner, loc, generic_params, adt_predicates, trait_id) } @@ -218,7 +218,6 @@ pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPr }); GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( Clauses::new_from_iter( - interner, adt_predicates .explicit_predicates() .iter_identity() @@ -233,11 +232,10 @@ pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPr } fn explicit_own_predicates<'db>( - interner: DbInterner<'db>, predicates: EarlyBinder<'db, impl Iterator>>, ) -> GenericPredicates { GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( - Clauses::new_from_iter(interner, predicates.skip_binder()).store(), + Clauses::new_from_iter(predicates.skip_binder()).store(), )) } @@ -292,8 +290,8 @@ fn simple_trait_predicates<'db>( parent: loc.adt.into(), local_id: param_idx, }); - let param_idx = - param_idx.into_raw().into_u32() + (generic_params.len_lifetimes() as u32); + let param_idx = param_idx.into_raw().into_u32() + + (generic_params.len_early_bound_lifetimes() as u32); let param_ty = Ty::new_param(interner, param_id, param_idx); let trait_args = trait_args(loc.trait_, param_ty); let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), trait_args); @@ -329,7 +327,6 @@ fn simple_trait_predicates<'db>( } GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( Clauses::new_from_iter( - interner, adt_predicates .explicit_predicates() .iter_identity() @@ -413,7 +410,7 @@ fn coerce_pointee_params<'db>( local_id: pointee_param, }); let pointee_param_idx = - pointee_param.into_raw().into_u32() + (generic_params.len_lifetimes() as u32); + pointee_param.into_raw().into_u32() + (generic_params.len_early_bound_lifetimes() as u32); let new_param_idx = generic_params.len() as u32; let new_param_id = coerce_pointee_new_type_param(trait_id); let new_param_ty = Ty::new_param(interner, new_param_id, new_param_idx); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index a1ed1e71aecc3..953e245a963a0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -125,7 +125,6 @@ fn intern_const_ref<'db>( { let u8_values = &interner.default_types().consts.u8_values; ValTreeKind::Branch(Consts::new_from_iter( - interner, value.as_str().as_bytes().iter().map(|&byte| u8_values[usize::from(byte)]), )) } @@ -351,6 +350,16 @@ pub(crate) fn path_to_const<'a, 'db>( Ok(Const::new_unevaluated(interner, UnevaluatedConst { def: konst.into(), args })) } +/// `forbid_params_after` and `allow_anon_const_using_generic_params` fill a similar role, but are not the same. +/// +/// `forbid_params_after` is only respected if an anon const is *not* created, that is, we directly resolve to a const param. +/// `allow_anon_const_using_generic_params` is only respected if an anon const *is* created. +/// +/// For example, inside types, you are allowed to refer to const params in bare paths (but might be forbidden from referring +/// to *some*, in param defaults), but not allowed to do so in anon consts (e.g. `{ {}; N }` is forbidden, because it creates +/// an anon const). Inside field defaults, you are allowed to refer to them even inside anon consts. +/// +/// There's also `generic_const_exprs`, `min_generic_const_args`, and `generic_const_args`, all of which we don't implement yet. pub(crate) fn create_anon_const<'a, 'db>( interner: DbInterner<'db>, owner: ExpressionStoreOwnerId, @@ -362,6 +371,7 @@ pub(crate) fn create_anon_const<'a, 'db>( create_var: Option<&mut dyn FnMut(Span) -> Const<'db>>, lowering_mode: LoweringMode, forbid_params_after: Option, + allow_anon_const_using_generic_params: bool, ) -> Result, CreateConstError<'db>> { let mut expr = &store[expr_id]; if let Expr::Block { statements, tail: Some(tail), .. } = expr @@ -397,18 +407,17 @@ pub(crate) fn create_anon_const<'a, 'db>( return Err(CreateConstError::AnonConstInterningDisabled); }; - let allow_using_generic_params = forbid_params_after.is_none(); let konst = AnonConstId::new( interner.db, AnonConstLoc { owner, expr: expr_id, ty: StoredEarlyBinder::bind(expected_ty.store()), - allow_using_generic_params, + allow_using_generic_params: allow_anon_const_using_generic_params, }, token, ); - let args = if allow_using_generic_params { + let args = if allow_anon_const_using_generic_params { GenericArgs::identity_for_item(interner, owner.generic_def(interner.db).into()) } else { GenericArgs::empty() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index b17df991f7afe..cb29e2178cc94 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -2528,6 +2528,33 @@ fn enums() { }) } +#[test] +fn unsupported_str_binary_op_in_eval_rvalue() { + check_fail( + r#" + enum E { + A = 1, + B = "a" + 1, + } + const GOAL: u8 = E::B as u8; + "#, + |e| matches!(e, ConstEvalError::MirEvalError(_)), + ); +} + +#[test] +fn enum_variant_as_usize_in_const() { + check_number( + r#" +enum Foo { + Bar = 4, +} +const GOAL: usize = Foo::Bar as usize; + "#, + 4, + ); +} + #[test] fn const_loop() { check_fail( @@ -2969,3 +2996,18 @@ fn recursive_adt() { }, ); } + +#[test] +fn uninhabited_enum_discriminant_read() { + check_fail( + r#" + #![feature(never_type)] + enum E { A(!), } + union U { u: (), e: E, } + const GOAL: () = { + let E::A(a) = unsafe { &(&U { u: () }).e }; + }; + "#, + |e| matches!(e, ConstEvalError::MirEvalError(MirEvalError::UndefinedBehavior(_))), + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 76c33d3cae7b2..afb34228da145 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -35,9 +35,8 @@ use crate::{ }, mir::{MirBody, MirLowerError}, next_solver::{ - Allocation, Clause, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredClauses, - StoredEarlyBinder, StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, TraitRef, - Ty, VariancesOf, + Allocation, Clause, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredEarlyBinder, + StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, TraitRef, Ty, VariancesOf, }, traits::{ParamEnvAndCrate, StoredParamEnvAndCrate}, }; @@ -319,7 +318,7 @@ pub trait HirDatabase: SourceDatabase + 'static { fn type_alias_bounds_with_diagnostics<'db>( &'db self, type_alias: TypeAliasId, - ) -> &'db TyLoweringResult<'db, TypeAliasBounds>> { + ) -> &'db TyLoweringResult<'db, TypeAliasBounds> { let db = self.as_dyn(); crate::lower::type_alias_bounds_with_diagnostics(db, type_alias) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 1eb2bf0f85bff..ec702b2f3a0a7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -343,7 +343,10 @@ impl<'db> ExprValidator<'db> { pat: PatId, initializer: Option, ) -> Option> { - if self.infer.pat_has_type_mismatch(pat) { + // Bail out if any subpattern has a type mismatch: the pattern isn't well-formed then, and + // deconstructing it can produce constructors with mismatching arities, which the + // usefulness analysis doesn't expect. `check_match` does the same for its arms. + if !types_of_subpatterns_do_match(pat, self.body, self.infer) { return None; } let initializer = initializer?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 4fd65398d0ba7..963de140a79fe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -19,7 +19,7 @@ use smallvec::SmallVec; use crate::{ ImplTraitId, db::{HirDatabase, InternedOpaqueTyId}, - lower::{GenericPredicates, associated_ty_item_bounds}, + lower::GenericPredicates, next_solver::{ AliasTy, Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, ParamTy, SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, Unnormalized, @@ -170,7 +170,7 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b // So, just return single boolean value for existence of such `Self` reference fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { GenericPredicates::query_explicit(db, trait_.into()).iter_identity().any(|pred| { - predicate_references_self(db, trait_, pred.skip_norm_wip(), AllowSelfProjection::No) + predicate_references_self(db, trait_, pred.skip_norm_wip(), AllowSelfProjections::No) }) } @@ -181,24 +181,22 @@ fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { .items .iter() .filter_map(|(_, it)| match *it { - AssocItemId::TypeAliasId(id) => Some(associated_ty_item_bounds(db, id)), + AssocItemId::TypeAliasId(id) => Some(id), _ => None, }) - .any(|bounds| { - bounds.skip_binder().iter().any(|pred| match pred.skip_binder() { - rustc_type_ir::ExistentialPredicate::Trait(it) => it.args.iter().any(|arg| { - contains_illegal_self_type_reference(db, trait_, &arg, AllowSelfProjection::Yes) - }), - rustc_type_ir::ExistentialPredicate::Projection(it) => it.args.iter().any(|arg| { - contains_illegal_self_type_reference(db, trait_, &arg, AllowSelfProjection::Yes) - }), - rustc_type_ir::ExistentialPredicate::AutoTrait(_) => false, - }) + .filter(|&assoc_type| !generics_require_sized_self(db, assoc_type.into())) + .flat_map(|assoc_type| { + db.type_alias_bounds(assoc_type).iter_identity_copied().map(Unnormalized::skip_norm_wip) + }) + .any(|clause| { + // Item bounds *can* have self projections, since they never get + // their self type erased. + predicate_references_self(db, trait_, clause, AllowSelfProjections::Yes) }) } #[derive(Clone, Copy)] -enum AllowSelfProjection { +enum AllowSelfProjections { Yes, No, } @@ -207,7 +205,7 @@ fn predicate_references_self<'db>( db: &'db dyn HirDatabase, trait_: TraitId, predicate: Clause<'db>, - allow_self_projection: AllowSelfProjection, + allow_self_projection: AllowSelfProjections, ) -> bool { match predicate.kind().skip_binder() { ClauseKind::Trait(trait_pred) => trait_pred.trait_ref.args.iter().skip(1).any(|arg| { @@ -226,13 +224,13 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable bool { struct IllegalSelfTypeVisitor<'db> { db: &'db dyn HirDatabase, trait_: TraitId, super_traits: Option>, - allow_self_projection: AllowSelfProjection, + allow_self_projection: AllowSelfProjections, } impl<'db> rustc_type_ir::TypeVisitor> for IllegalSelfTypeVisitor<'db> { type Result = ControlFlow<()>; @@ -248,7 +246,7 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable match self.allow_self_projection { - AllowSelfProjection::Yes => { + AllowSelfProjections::Yes => { let trait_ = proj.trait_def_id(interner).0; if self.super_traits.is_none() { self.super_traits = Some( @@ -263,7 +261,7 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable ty.super_visit_with(self), + AllowSelfProjections::No => ty.super_visit_with(self), }, _ => ty.super_visit_with(self), } @@ -336,7 +334,12 @@ where let sig = db.callable_item_signature(func.into()); if sig.skip_binder().inputs().iter().skip(1).any(|ty| { - contains_illegal_self_type_reference(db, trait_, ty.skip_binder(), AllowSelfProjection::Yes) + contains_illegal_self_type_reference( + db, + trait_, + ty.skip_binder(), + AllowSelfProjections::Yes, + ) }) { cb(MethodViolationCode::ReferencesSelfInput)?; } @@ -345,7 +348,7 @@ where db, trait_, &sig.skip_binder().output(), - AllowSelfProjection::Yes, + AllowSelfProjections::Yes, ) { cb(MethodViolationCode::ReferencesSelfOutput)?; } @@ -386,7 +389,7 @@ where continue; } - if contains_illegal_self_type_reference(db, trait_, &pred, AllowSelfProjection::Yes) { + if contains_illegal_self_type_reference(db, trait_, &pred, AllowSelfProjections::Yes) { cb(MethodViolationCode::WhereClauseReferencesSelf)?; break; } @@ -454,7 +457,6 @@ fn receiver_is_dispatchable<'db>( ParamEnv { clauses: Clauses::new_from_iter( - interner, generic_predicates .iter_identity() .map(Unnormalized::skip_norm_wip) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index f2ca060bb5335..1ebaa3d12290d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -69,15 +69,11 @@ impl<'db> SingleGenerics<'db> { } pub(crate) fn len_lifetimes(&self) -> usize { - self.params.len_lifetimes() + self.params.len_early_bound_lifetimes() } pub(crate) fn len(&self, consider_late_bound: bool) -> usize { - if consider_late_bound { - self.params.len() - } else { - self.params.len() - self.params.len_late_bound_lifetimes() - } + if consider_late_bound { self.params.len() } else { self.params.len_no_late() } } fn iter_lifetimes(&self) -> impl Iterator { @@ -269,7 +265,7 @@ impl<'db> Generics<'db> { let parent_total = self.len_parent(); let owner = self.owner(); - let lifetimes = owner.params.len_lifetimes(); + let lifetimes = owner.params.len_early_bound_lifetimes(); let mut has_self_param = false; let mut non_impl_trait_type_params = 0; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index cbe5b671fe83a..464d4f6640f10 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -33,6 +33,7 @@ pub(crate) mod unify; use std::{ cell::{OnceCell, RefCell}, + cmp, convert::identity, fmt, hash::Hash, @@ -1060,16 +1061,32 @@ pub struct CaptureInfo { /// during `typeck`, specifically by `regionck`. #[derive(Eq, PartialEq, Clone, Debug, Copy, Hash)] pub enum UpvarCapture { + /// Upvar is captured by reference. + ByRef(BorrowKind), + + /// Upvar is captured by use. This is true when the closure is labeled `use`. + ByUse, + /// Upvar is captured by value. This is always true when the /// closure is labeled `move`, but can also be true in other cases /// depending on inference. ByValue, +} - /// Upvar is captured by use. This is true when the closure is labeled `use`. - ByUse, - - /// Upvar is captured by reference. - ByRef(BorrowKind), +// Used in closure::analysis::determine_capture_info +impl PartialOrd for UpvarCapture { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Self::ByValue, Self::ByValue) | (Self::ByUse, Self::ByUse) => { + Some(cmp::Ordering::Equal) + } + (Self::ByValue | Self::ByUse, Self::ByRef(_)) => Some(cmp::Ordering::Greater), + (Self::ByRef(_), Self::ByValue | Self::ByUse) => Some(cmp::Ordering::Less), + (Self::ByRef(left), Self::ByRef(right)) => Some(left.cmp(right)), + (Self::ByUse, Self::ByValue) | (Self::ByValue, Self::ByUse) => None, + } + } } #[salsa::tracked] @@ -1964,12 +1981,7 @@ impl<'db> InferenceContext<'db> { }) } - pub(crate) fn create_anon_const( - &mut self, - expr: ExprId, - expected_ty: Ty<'db>, - allow_using_generic_params: bool, - ) -> Const<'db> { + pub(crate) fn create_anon_const(&mut self, expr: ExprId, expected_ty: Ty<'db>) -> Const<'db> { never!(expected_ty.has_infer(), "cannot have infer vars in an anon const's ty"); let konst = create_anon_const( self.interner(), @@ -1981,7 +1993,11 @@ impl<'db> InferenceContext<'db> { &|| self.generics(), Some(&mut |span| self.table.next_const_var(span)), self.lowering_mode, - (!(allow_using_generic_params && self.allow_using_generic_params)).then_some(0), + (!self.allow_using_generic_params).then_some(0), + // The situation around body anon consts using generic params is complicated. + // rustc disallows them all, except for array repeat expressions. However in another place even repeat exprs + // are disallowed, so in practice they cause an error. + false, ); if let Ok(konst) = konst diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index dc08139081947..9177e335b75de 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -345,28 +345,26 @@ impl<'db> CastCheck<'db> { // We also need to skip auto traits to emit an FCW and not an error. let src_obj = Ty::new_dynamic( ctx.interner(), - BoundExistentialPredicates::new_from_iter( - ctx.interner(), - src_tty.iter().filter(|pred| { + BoundExistentialPredicates::new_from_iter(src_tty.iter().filter( + |pred| { !matches!( pred.skip_binder(), ExistentialPredicate::AutoTrait(_) ) - }), - ), + }, + )), Region::new_erased(ctx.interner()), ); let dst_obj = Ty::new_dynamic( ctx.interner(), - BoundExistentialPredicates::new_from_iter( - ctx.interner(), - dst_tty.iter().filter(|pred| { + BoundExistentialPredicates::new_from_iter(dst_tty.iter().filter( + |pred| { !matches!( pred.skip_binder(), ExistentialPredicate::AutoTrait(_) ) - }), - ), + }, + )), Region::new_erased(ctx.interner()), ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 4ce7e3a72b956..830b6dd124c40 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -35,7 +35,7 @@ use crate::{ }, }; -use super::{Expectation, InferenceContext}; +use super::{Expectation, InferenceContext, unify::UseSubtyping}; #[derive(Debug)] struct ClosureSignatures<'db> { @@ -273,9 +273,9 @@ impl<'db> InferenceContext<'db> { // coroutine. To do so, we use the `CoroutineClosureSignature` to compute // the coroutine type, filling in the tupled_upvars_ty and kind_ty with infer // vars which will get constrained during upvar analysis. - let coroutine_output_ty = closure_args - .coroutine_closure_sig() - .map_bound(|sig| { + let coroutine_output_ty = interner.liberate_late_bound_regions( + self.owner.into(), + closure_args.coroutine_closure_sig().map_bound(|sig| { sig.to_coroutine( interner, parent_args.as_slice(), @@ -283,8 +283,8 @@ impl<'db> InferenceContext<'db> { interner.coroutine_for_closure(coroutine_closure_id.into()), coroutine_upvars_ty, ) - }) - .skip_binder(); + }), + ); liberated_sig = interner.mk_fn_sig( liberated_sig.inputs().iter().copied(), coroutine_output_ty, @@ -400,7 +400,10 @@ impl<'db> InferenceContext<'db> { closure_expr, Ty::new_var(self.interner(), self.table.infer_ctxt.root_var(vid)), closure_kind, - self.table.obligations_for_self_ty(vid).into_iter().map(|obl| obl.predicate), + self.table + .obligations_for_self_ty(vid, UseSubtyping::No) + .into_iter() + .map(|obl| obl.predicate), ), TyKind::FnPtr(sig_tys, hdr) => match closure_kind { ClosureKind::Closure => { @@ -663,7 +666,7 @@ impl<'db> InferenceContext<'db> { // FIXME: We may want to elaborate here, though I assume this will be exceedingly rare. let mut return_ty = None; - for bound in self.table.obligations_for_self_ty(return_vid) { + for bound in self.table.obligations_for_self_ty(return_vid, UseSubtyping::No) { if let PredicateKind::Clause(ClauseKind::Projection(ret_projection)) = bound.predicate.kind().skip_binder() && let ret_projection = bound.predicate.kind().rebind(ret_projection) @@ -1054,7 +1057,7 @@ impl<'db> InferenceContext<'db> { let output_ty = match ret_ty.kind() { TyKind::Infer(InferTy::TyVar(ret_vid)) => self .table - .obligations_for_self_ty(ret_vid) + .obligations_for_self_ty(ret_vid, UseSubtyping::No) .into_iter() .find_map(|obligation| get_future_output(obligation.predicate))?, TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index 0c24b82d1bde0..bdce6ae8cd939 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -30,7 +30,7 @@ //! then mean that all later passes would have to check for these figments //! and report an error, and it just seems like more mess in the end.) -use std::{iter, mem}; +use std::{cmp, iter, mem}; use hir_def::{ expr_store::ExpressionStore, @@ -88,7 +88,7 @@ impl<'db> UpvarArgs<'db> { } } -#[derive(Eq, Clone, PartialEq, Debug, Copy, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum BorrowKind { /// Data must be immutable and is aliasable. Immutable, @@ -1295,9 +1295,9 @@ fn restrict_precision_for_drop_types<'db>( mut place: Place, capture_info: &mut CaptureInfo, ) -> Place { - let is_copy_type = fcx.infcx().type_is_copy_modulo_regions(fcx.table.param_env, place.ty()); - - if let (false, UpvarCapture::ByValue) = (is_copy_type, capture_info.capture_kind) { + if capture_info.capture_kind == UpvarCapture::ByValue + && !fcx.infcx().type_is_copy_modulo_regions(fcx.table.param_env, place.ty()) + { for i in 0..place.projections.len() { match place.ty_before_projection(i).kind() { TyKind::Adt(def, _) if def.destructor(fcx.interner()).is_some() => { @@ -1407,53 +1407,12 @@ fn adjust_for_non_move_closure(mut place: Place, capture_info: &mut CaptureInfo) /// At the end, `capture_info_a` will contain the selected info. fn determine_capture_info(capture_info_a: &mut CaptureInfo, capture_info_b: &mut CaptureInfo) { - // If the capture kind is equivalent then, we don't need to escalate and can compare the - // expressions. - let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (UpvarCapture::ByValue, UpvarCapture::ByValue) => true, - (UpvarCapture::ByUse, UpvarCapture::ByUse) => true, - (UpvarCapture::ByRef(ref_a), UpvarCapture::ByRef(ref_b)) => ref_a == ref_b, - (UpvarCapture::ByValue, _) | (UpvarCapture::ByUse, _) | (UpvarCapture::ByRef(_), _) => { - false - } - }; - - let swap = if eq_capture_kind { - false - } else { - // We select the CaptureKind which ranks higher based the following priority order: - // (ByUse | ByValue) > MutBorrow > UniqueImmBorrow > ImmBorrow - match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (UpvarCapture::ByUse, UpvarCapture::ByValue) - | (UpvarCapture::ByValue, UpvarCapture::ByUse) => { - panic!("Same capture can't be ByUse and ByValue at the same time") - } - (UpvarCapture::ByValue, UpvarCapture::ByValue) - | (UpvarCapture::ByUse, UpvarCapture::ByUse) - | (UpvarCapture::ByValue | UpvarCapture::ByUse, UpvarCapture::ByRef(_)) => false, - (UpvarCapture::ByRef(_), UpvarCapture::ByValue | UpvarCapture::ByUse) => true, - (UpvarCapture::ByRef(ref_a), UpvarCapture::ByRef(ref_b)) => { - match (ref_a, ref_b) { - // Take LHS: - (BorrowKind::UniqueImmutable | BorrowKind::Mutable, BorrowKind::Immutable) - | (BorrowKind::Mutable, BorrowKind::UniqueImmutable) => false, - - // Take RHS: - (BorrowKind::Immutable, BorrowKind::UniqueImmutable | BorrowKind::Mutable) - | (BorrowKind::UniqueImmutable, BorrowKind::Mutable) => true, - - (BorrowKind::Immutable, BorrowKind::Immutable) - | (BorrowKind::UniqueImmutable, BorrowKind::UniqueImmutable) - | (BorrowKind::Mutable, BorrowKind::Mutable) => { - panic!("Expected unequal capture kinds"); - } - } - } - } - }; - - if swap { - mem::swap(capture_info_a, capture_info_b); + // We select the CaptureKind which ranks higher based the following priority order: + // (ByUse | ByValue) > MutBorrow > UniqueImmBorrow > ImmBorrow + match capture_info_a.capture_kind.partial_cmp(&capture_info_b.capture_kind) { + Some(cmp::Ordering::Equal) | Some(cmp::Ordering::Greater) => {} + Some(cmp::Ordering::Less) => mem::swap(capture_info_a, capture_info_b), + None => panic!("Same capture can't be ByUse and ByValue at the same time"), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 85d8142335dd3..7c0ec0738fb46 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -1024,7 +1024,14 @@ impl<'db> InferenceContext<'db> { } } }; - if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) { + if let (Some(mut a_sig), Some(mut b_sig)) = (a_sig, b_sig) { + // Allow coercing safe sigs to unsafe sigs + if a_sig.safety().is_safe() && !b_sig.safety().is_safe() { + a_sig = a_sig.map_bound(|sig| sig.set_safety(Safety::Unsafe)); + } else if b_sig.safety().is_safe() && !a_sig.safety().is_safe() { + b_sig = b_sig.map_bound(|sig| sig.set_safety(Safety::Unsafe)); + } + // The signature must match. let sig = self .table diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 8c9e814015897..1801af83ade0e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1309,7 +1309,7 @@ impl<'db> InferenceContext<'db> { expr: ExprId, ) -> Ty<'db> { let interner = self.interner(); - let count_ct = self.create_anon_const(count, self.types.types.usize, true); + let count_ct = self.create_anon_const(count, self.types.types.usize); let count = self.table.try_structurally_resolve_const(count.into(), count_ct); let uty = match expected { @@ -1485,8 +1485,7 @@ impl<'db> InferenceContext<'db> { PatOrigin::LetStmt { has_else: else_branch.is_some() }, ); if let Some(expr) = else_branch { - let previous_diverges = - mem::replace(&mut this.diverges, Diverges::Maybe); + let previous_diverges = this.diverges; this.infer_expr_coerce( *expr, &Expectation::HasType(this.types.types.never), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 17c198879f265..24617c512966d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -1120,7 +1120,7 @@ impl<'db> InferenceContext<'db> { let element_tys_iter = (0..max_len).map(|i| { self.table.next_ty_var(elements.get(i).copied().map(Span::PatId).unwrap_or(Span::Dummy)) }); - let element_tys = Tys::new_from_iter(interner, element_tys_iter); + let element_tys = Tys::new_from_iter(element_tys_iter); let pat_ty = Ty::new(interner, TyKind::Tuple(element_tys)); if self.demand_eqtype(pat.into(), expected, pat_ty).is_err() { let expected = if let TyKind::Tuple(tys) = diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 8070ed8788977..558e9eafd895d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -34,11 +34,27 @@ use crate::{ traits::ParamEnvAndCrate, }; +/// Whether to use subtyping or not when inspecting obligations. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum UseSubtyping { + /// Do **not** use subtyping. [`InferenceTable::obligations_for_self_ty`] will only return + /// obligations where the self type is known to be equal to the provided vid. + No, + + /// Use subtyping. [`InferenceTable::obligations_for_self_ty`] will return obligations + /// where the self type is related to the provided vid via subtyping. + /// + /// Using this requires extra care, as traits holding for a subtype or a supertype, does not + /// necessarily imply that they hold for the respective supertype or subtype. + Yes, +} + struct NestedObligationsForSelfTy<'a, 'db> { ctx: &'a InferenceTable<'db>, self_ty: TyVid, root_cause: &'a ObligationCause, obligations_for_self_ty: &'a mut SmallVec<[Obligation<'db, Predicate<'db>>; 4]>, + subtyping: UseSubtyping, } impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { @@ -64,7 +80,7 @@ impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { let db = self.ctx.interner(); let goal = inspect_goal.goal(); - if self.ctx.predicate_has_self_ty(goal.predicate, self.self_ty) { + if self.ctx.predicate_has_self_ty(goal.predicate, self.self_ty, self.subtyping) { self.obligations_for_self_ty.push(Obligation::new( db, *self.root_cause, @@ -180,7 +196,10 @@ impl<'db> InferenceTable<'db> { let Some(sized_did) = self.interner().lang_items().Sized else { return true; }; - self.obligations_for_self_ty(self_ty).into_iter().any(|obligation| { + // NB: `T: Sized` implies that all subtypes and all supertypes of `T` are also sized, + // so it's valid to use subtyping here. (subtyping has to preserve layout and + // `T <: U => &T <: &U`, so subtyping can't change sizedness) + self.obligations_for_self_ty(self_ty, UseSubtyping::Yes).into_iter().any(|obligation| { match obligation.predicate.kind().skip_binder() { PredicateKind::Clause(ClauseKind::Trait(data)) => data.def_id().0 == sized_did, _ => false, @@ -191,6 +210,7 @@ impl<'db> InferenceTable<'db> { pub(super) fn obligations_for_self_ty( &self, self_ty: TyVid, + subtyping: UseSubtyping, ) -> SmallVec<[Obligation<'db, Predicate<'db>>; 4]> { let obligations = self.fulfillment_cx.pending_obligations(); let mut obligations_for_self_ty = SmallVec::new(); @@ -200,6 +220,7 @@ impl<'db> InferenceTable<'db> { self_ty, obligations_for_self_ty: &mut obligations_for_self_ty, root_cause: &obligation.cause, + subtyping, }; let goal = obligation.as_goal(); @@ -213,14 +234,21 @@ impl<'db> InferenceTable<'db> { obligations_for_self_ty } - fn predicate_has_self_ty(&self, predicate: Predicate<'db>, expected_vid: TyVid) -> bool { + fn predicate_has_self_ty( + &self, + predicate: Predicate<'db>, + expected_vid: TyVid, + subtyping: UseSubtyping, + ) -> bool { match predicate.kind().skip_binder() { PredicateKind::Clause(ClauseKind::Trait(data)) => { - self.type_matches_expected_vid(expected_vid, data.self_ty()) - } - PredicateKind::Clause(ClauseKind::Projection(data)) => { - self.type_matches_expected_vid(expected_vid, data.projection_term.self_ty()) + self.type_matches_expected_vid(expected_vid, data.self_ty(), subtyping) } + PredicateKind::Clause(ClauseKind::Projection(data)) => self.type_matches_expected_vid( + expected_vid, + data.projection_term.self_ty(), + subtyping, + ), PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) | PredicateKind::Subtype(..) | PredicateKind::Coerce(..) @@ -238,13 +266,24 @@ impl<'db> InferenceTable<'db> { } } - fn type_matches_expected_vid(&self, expected_vid: TyVid, ty: Ty<'db>) -> bool { + fn type_matches_expected_vid( + &self, + expected_vid: TyVid, + ty: Ty<'db>, + subtyping: UseSubtyping, + ) -> bool { let ty = self.shallow_resolve(ty); match ty.kind() { - TyKind::Infer(rustc_type_ir::TyVar(found_vid)) => { - self.infer_ctxt.root_var(expected_vid) == self.infer_ctxt.root_var(found_vid) - } + TyKind::Infer(rustc_type_ir::TyVar(found_vid)) => match subtyping { + UseSubtyping::No => { + self.infer_ctxt.root_var(expected_vid) == self.infer_ctxt.root_var(found_vid) + } + UseSubtyping::Yes => { + self.infer_ctxt.sub_unification_table_root_var(expected_vid) + == self.infer_ctxt.sub_unification_table_root_var(found_vid) + } + }, _ => false, } } 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 04e453fde3b93..910a7c6140d50 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -44,11 +44,14 @@ use rustc_abi::ExternAbi; use rustc_ast_ir::Mutability; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, BoundRegion, BoundRegionKind, BoundTyKind, BoundVar, BoundVariableKind, + AliasTyKind, BoundRegion, BoundRegionKind, BoundTyKind, BoundVar, BoundVarIndexKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, - Interner, OutlivesPredicate, TermKind, TyKind, TypeFoldable, TypeVisitableExt, Upcast, - UpcastFrom, elaborate, - inherent::{Clause as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _}, + Interner, OutlivesPredicate, TermKind, TyKind, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, Upcast, UpcastFrom, elaborate, + inherent::{ + Clause as _, GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _, + }, + shift_vars, }; use salsa::SalsaValue; use smallvec::SmallVec; @@ -66,13 +69,13 @@ use crate::{ generics::{Generics, SingleGenerics, generics}, infer::unify::InferenceTable, next_solver::{ - AliasTy, Binder, BoundExistentialPredicates, BoundVarKinds, Clause, ClauseKind, Clauses, - Const, ConstKind, DbInterner, DefaultAny, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, - FnSigKind, FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PatList, Pattern, - PolyFnSig, Predicate, Region, StoredClauses, StoredConst, StoredEarlyBinder, - StoredGenericArg, StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, - TraitPredicate, TraitRef, Ty, Tys, Unnormalized, abi::Safety, mk_param, - util::BottomUpFolder, + AliasTy, Binder, BoundConst, BoundExistentialPredicates, BoundTy, BoundVarKinds, + BoundVariableKind, Clause, ClauseKind, Clauses, Const, ConstKind, DbInterner, DefaultAny, + EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FnSigKind, FxIndexMap, GenericArg, + GenericArgKind, GenericArgs, ParamConst, ParamEnv, PatList, Pattern, PolyFnSig, Predicate, + Region, RegionKind, StoredClauses, StoredConst, StoredEarlyBinder, StoredGenericArg, + StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, + Tys, Unnormalized, abi::Safety, default_types, mk_param, util::BottomUpFolder, }, }; @@ -247,7 +250,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { let in_binders = DebruijnIndex::ZERO; let interner = DbInterner::new_with(db, resolver.krate()); let bound_vars = - vec![(Vec::new(), TyLoweringContext::bound_vars(db, interner, generic_def, generics))]; + vec![(Vec::new(), TyLoweringContext::bound_vars(db, generic_def, generics))]; Self { db, // Can provide no block since we don't use it for trait solving. @@ -284,7 +287,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.def = ExpressionStoreOwnerId::Signature(owner.def()); } - pub(crate) fn with_interning_mode(mut self, interning_mode: LoweringMode) -> Self { + pub fn with_interning_mode(mut self, interning_mode: LoweringMode) -> Self { self.interning_mode = interning_mode; self } @@ -374,12 +377,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } fn push_bound_vars(&mut self, binder: &[Name]) { - let bound_vars = BoundVarKinds::new_from_iter( - self.interner, - binder.iter().map(|_| { + let bound_vars = + BoundVarKinds::new_from_iter(binder.iter().map(|_| { BoundVariableKind::Region(BoundRegionKind::Named(self.generic_def.into())) - }), - ); + })); self.bound_vars.push((binder.to_vec(), bound_vars)); } @@ -393,7 +394,6 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { fn bound_vars( db: &'db dyn HirDatabase, - interner: DbInterner<'db>, def: GenericDefId, generic: &'a OnceCell>, ) -> BoundVarKinds<'db> { @@ -410,7 +410,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } }); - BoundVarKinds::new_from_iter(interner, args) + BoundVarKinds::new_from_iter(args) } fn take_defined_opaques(&mut self) -> Option>> { @@ -450,14 +450,20 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.lower_ty_ext(type_ref).0 } - pub(crate) fn lower_const(&mut self, const_ref: ConstRef, const_type: Ty<'db>) -> Const<'db> { - self.lower_expr_as_const(const_ref.expr, const_type) + pub(crate) fn lower_const( + &mut self, + const_ref: ConstRef, + const_type: Ty<'db>, + allow_anon_const_using_generic_params: bool, + ) -> Const<'db> { + self.lower_expr_as_const(const_ref.expr, const_type, allow_anon_const_using_generic_params) } pub(crate) fn lower_expr_as_const( &mut self, expr_id: ExprId, const_type: Ty<'db>, + allow_anon_const_using_generic_params: bool, ) -> Const<'db> { #[expect(clippy::manual_map, reason = "a `map()` here generates a borrowck error")] let create_var = match &mut self.infer_vars { @@ -477,6 +483,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { create_var, self.interning_mode, self.forbid_params_after, + allow_anon_const_using_generic_params, ); if let Ok(konst) = konst @@ -603,7 +610,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } TypeRef::Array(array) => { let inner_ty = self.lower_ty(array.ty); - let const_len = self.lower_const(array.len, self.types.types.usize); + let const_len = self.lower_const(array.len, self.types.types.usize, false); Ty::new_array_with_const_len(interner, inner_ty, const_len) } &TypeRef::Slice(inner) => { @@ -729,15 +736,17 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { fn lower_pattern_type(&mut self, pat: PatId, ty: Ty<'db>) -> Option> { let pat_kind = match self.store[pat] { hir_def::hir::Pat::Range { start: Some(start), end: Some(end), range_type: _ } => { + // rustc allows using generic params in pattern types. + // In practice this does not matter since this feature is internal and I didn't find a way to actually use generic params, + // but it doesn't matter so... rustc_type_ir::PatternKind::Range { - start: self.lower_expr_as_const(start, ty), - end: self.lower_expr_as_const(end, ty), + start: self.lower_expr_as_const(start, ty, true), + end: self.lower_expr_as_const(end, ty, true), } } hir_def::hir::Pat::NotNull => rustc_type_ir::PatternKind::NotNull, hir_def::hir::Pat::Or(ref pats) => rustc_type_ir::PatternKind::Or( PatList::new_from_iter( - self.interner, pats.iter().map(|&pat| self.lower_pattern_type(pat, ty).ok_or(())), ) .ok()?, @@ -880,7 +889,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { fn lower_trait_ref( &mut self, - trait_ref: &HirTraitRef, + trait_ref: HirTraitRef, explicit_self_ty: Ty<'db>, ) -> Option> { self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty).map(|it| it.0) @@ -1263,7 +1272,6 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { // N.b. principal, projections, auto traits Some(BoundExistentialPredicates::new_from_iter( - interner, principal.into_iter().chain(projections).chain(auto_traits), )) }; @@ -1494,7 +1502,7 @@ pub(crate) fn impl_trait_with_diagnostics<'db>( LifetimeLoweringMode::Bound, ); let self_ty = db.impl_self_ty(impl_id).skip_binder(); - let target_trait = impl_data.target_trait.as_ref()?; + let target_trait = impl_data.target_trait?; let trait_ref = ctx.lower_trait_ref(target_trait, self_ty)?; Some(TyLoweringResult::from_ctx(StoredEarlyBinder::bind(StoredTraitRef::new(trait_ref)), ctx)) } @@ -1846,26 +1854,24 @@ pub(crate) fn impl_self_ty_with_diagnostics_cycle_result<'db>( } pub(crate) fn const_param_ty<'db>(db: &'db dyn HirDatabase, def: ConstParamId) -> Ty<'db> { - let param_types = const_param_types(db, def.parent()); - match param_types.get(def.local_id()) { - Some(ty) => ty.as_ref(), - None => Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed), - } + const_param_types(db, def.parent()).get(def.local_id()) } /// Wrapper struct around `ArenaMap` which implements [`SalsaValue`]. /// /// Required to make the `SalsaValue` derive for [`TyLoweringResult`] work. -#[derive(Default, PartialEq, Eq, SalsaValue)] +#[derive(Debug, Default, PartialEq, Eq, SalsaValue)] pub struct ConstParamTypes { map: ArenaMap, } -impl Deref for ConstParamTypes { - type Target = ArenaMap; - - fn deref(&self) -> &Self::Target { - &self.map +impl ConstParamTypes { + #[inline] + pub fn get(&self, local_id: LocalTypeOrConstParamId) -> Ty<'_> { + match self.map.get(local_id) { + Some(ty) => ty.as_ref(), + None => default_types().types.error, + } } } @@ -1979,7 +1985,7 @@ pub(crate) fn field_types_with_diagnostics<'db>( ); for (field_id, field_data) in var_data.fields().iter() { let ty = ctx.lower_ty(field_data.type_ref); - let default = field_data.default_value.map(|default| ctx.lower_const(default, ty)); + let default = field_data.default_value.map(|default| ctx.lower_const(default, ty, true)); res.insert( field_id, FieldType { @@ -2149,9 +2155,8 @@ fn resolve_type_param_assoc_type_shorthand( if target != param.into() { continue; } - let Some(TypeNs::TraitId(bounded_trait)) = - resolver.resolve_path_in_type_ns_fully(db, &ctx.store[bounded_trait_path]) - else { + let mut ctx = ctx.at_path(bounded_trait_path); + let Some(TypeNs::TraitId(bounded_trait)) = ctx.resolve_path_in_type_ns_fully() else { continue; }; if !SupertraitsInfo::query(db, bounded_trait) @@ -2162,11 +2167,8 @@ fn resolve_type_param_assoc_type_shorthand( continue; } - let Some((bounded_trait_ref, _)) = - ctx.lower_trait_ref_from_path(bounded_trait_path, param_ty) - else { - continue; - }; + let bounded_trait_ref = + ctx.lower_trait_ref_from_resolved_path(bounded_trait, param_ty, false, Span::Dummy); // Now, search from the start on the *bounded* trait like if we wrote `Self::Assoc`. Eventually, we'll get // the correct trait ref (or a cycle). let lookup_on_bounded_trait = resolve_type_param_assoc_type_shorthand( @@ -2272,8 +2274,8 @@ pub(crate) fn type_alias_self_bounds<'db>( } #[derive(PartialEq, Eq, Debug, Hash, SalsaValue)] -pub struct TypeAliasBounds { - predicates: T, +pub struct TypeAliasBounds { + predicates: StoredEarlyBinder, assoc_ty_bounds_start: u32, } @@ -2281,7 +2283,7 @@ pub struct TypeAliasBounds { pub(crate) fn type_alias_bounds_with_diagnostics<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, -) -> TyLoweringResult<'db, TypeAliasBounds>> { +) -> TyLoweringResult<'db, TypeAliasBounds> { let type_alias_data = TypeAliasSignature::of(db, type_alias); let resolver = type_alias.resolver(db); let generics = OnceCell::new(); @@ -2313,6 +2315,27 @@ pub(crate) fn type_alias_bounds_with_diagnostics<'db>( }); } + if let ItemContainerId::TraitId(parent_trait) = type_alias.loc(db).container { + let item_trait_ref = TraitRef::identity(interner, parent_trait.into()); + let parent_predicates = GenericPredicates::query_explicit(db, parent_trait.into()); + parent_predicates.iter_identity().for_each(|parent_predicate| { + let parent_predicate = parent_predicate.skip_norm_wip(); + let Some((predicate, source)) = remap_gat_vars_and_recurse_into_nested_projections( + interner, + item_trait_ref, + type_alias, + &generics, + parent_predicate, + ) else { + return; + }; + match source { + GenericPredicateSource::SelfOnly => bounds.push(predicate), + GenericPredicateSource::AssocTyBound => assoc_ty_bounds.push(predicate), + } + }); + } + if !ctx.unsized_types.contains(&interner_ty) { let sized_trait = ctx.lang_items.Sized; if let Some(sized_trait) = sized_trait { @@ -2337,21 +2360,254 @@ pub(crate) fn type_alias_bounds_with_diagnostics<'db>( ) } +/// The code below is quite involved, so let me explain. +/// +/// We loop here, because we also want to collect vars for nested associated items as +/// well. For example, given a clause like `Self::A::B`, we want to add that to the +/// item bounds for `A`, so that we may use that bound in the case that `Self::A::B` is +/// rigid. +/// +/// Secondly, regarding bound vars, when we see a where clause that mentions a GAT +/// like `for<'a, ...> Self::Assoc<'a, ...>: Bound<'b, ...>`, we want to turn that into +/// an item bound on the GAT, where all of the GAT args are substituted with the GAT's +/// param regions, and then keep all of the other late-bound vars in the bound around. +/// We need to "compress" the binder so that it doesn't mention any of those vars that +/// were mapped to params. +fn remap_gat_vars_and_recurse_into_nested_projections<'db>( + interner: DbInterner<'db>, + item_trait_ref: TraitRef<'db>, + assoc_item_def_id: TypeAliasId, + generics: &OnceCell>, + clause: Clause<'db>, +) -> Option<(Clause<'db>, GenericPredicateSource)> { + let mut clause_ty = match clause.kind().skip_binder() { + ClauseKind::Trait(tr) => tr.self_ty(), + ClauseKind::Projection(proj) => proj.projection_term.self_ty(), + ClauseKind::TypeOutlives(outlives) => outlives.0, + ClauseKind::HostEffect(host) => host.self_ty(), + _ => return None, + }; + + let mut predicate_source = GenericPredicateSource::SelfOnly; + let gat_vars = loop { + if let TyKind::Alias( + alias_ty @ AliasTy { kind: AliasTyKind::Projection { def_id: alias_ty_def_id }, .. }, + ) = clause_ty.kind() + { + if alias_ty.trait_ref(interner) == item_trait_ref + && alias_ty_def_id.0 == assoc_item_def_id + { + // We have found the GAT in question... + // Return the vars, since we may need to remap them. + break &alias_ty.args.as_slice()[item_trait_ref.args.len()..]; + } else { + predicate_source = GenericPredicateSource::AssocTyBound; + + clause_ty = alias_ty.self_ty(); + continue; + } + } + + return None; + }; + + // Special-case: No GAT vars, no mapping needed. + if gat_vars.is_empty() { + return Some((clause, predicate_source)); + } + + // First, check that all of the GAT args are substituted with a unique late-bound arg. + // If we find a duplicate, then it can't be mapped to the definition's params. + let mut mapping = FxIndexMap::default(); + let generics = generics + .get_or_init(|| crate::generics::generics(interner.db, assoc_item_def_id.into())) + .owner(); + for ((param_idx, param, _), var) in std::iter::zip(generics.iter_with_idx(), gat_vars) { + let existing = match var.kind() { + GenericArgKind::Lifetime(re) => { + let RegionKind::ReBound(BoundVarIndexKind::Bound(debruijn), bv) = re.kind() else { + return None; + }; + if debruijn != rustc_type_ir::INNERMOST { + return None; + } + mapping.insert(bv.var, mk_param(interner, param_idx, param)) + } + GenericArgKind::Type(ty) => { + let TyKind::Bound(BoundVarIndexKind::Bound(debruijn), bv) = ty.kind() else { + return None; + }; + if debruijn != rustc_type_ir::INNERMOST { + return None; + } + mapping.insert(bv.var, mk_param(interner, param_idx, param)) + } + GenericArgKind::Const(ct) => { + let ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), bv) = ct.kind() else { + return None; + }; + if debruijn != rustc_type_ir::INNERMOST { + return None; + } + mapping.insert(bv.var, mk_param(interner, param_idx, param)) + } + }; + + if existing.is_some() { + return None; + } + } + + // Finally, map all of the args in the GAT to the params we expect, and compress + // the remaining late-bound vars so that they count up from var 0. + let mut folder = MapAndCompressBoundVars { + tcx: interner, + binder: rustc_type_ir::INNERMOST, + still_bound_vars: vec![], + mapping, + }; + let pred = clause.kind().skip_binder().fold_with(&mut folder); + + Some(( + Binder::bind_with_vars(pred, BoundVarKinds::new_from_slice(&folder.still_bound_vars)) + .upcast(interner), + predicate_source, + )) +} + +/// Given some where clause like `for<'b, 'c> >::Gat<'b>: Bound<'c>`, +/// the mapping will map `'b` back to the GAT's `'b_identity`. Then we need to compress the +/// remaining bound var `'c` to index 0. +/// +/// This folder gives us: `for<'c> >::Gat<'b_identity>: Bound<'c>`, +/// which is sufficient for an item bound for `Gat`, since all of the GAT's args are identity. +struct MapAndCompressBoundVars<'db> { + tcx: DbInterner<'db>, + /// How deep are we? Makes sure we don't touch the vars of nested binders. + binder: DebruijnIndex, + /// List of bound vars that remain unsubstituted because they were not + /// mentioned in the GAT's args. + still_bound_vars: Vec>, + /// Subtle invariant: If the `GenericArg` is bound, then it should be + /// stored with the debruijn index of `INNERMOST` so it can be shifted + /// correctly during substitution. + mapping: FxIndexMap>, +} + +impl<'db> TypeFolder> for MapAndCompressBoundVars<'db> { + fn cx(&self) -> DbInterner<'db> { + self.tcx + } + + fn fold_binder(&mut self, t: Binder<'db, T>) -> Binder<'db, T> + where + Binder<'db, T>: TypeSuperFoldable>, + { + self.binder.shift_in(1); + let out = t.super_fold_with(self); + self.binder.shift_out(1); + out + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + if !ty.has_bound_vars() { + return ty; + } + + if let TyKind::Bound(BoundVarIndexKind::Bound(binder), old_bound) = ty.kind() + && self.binder == binder + { + let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) { + mapped.expect_ty() + } else { + // If we didn't find a mapped generic, then make a new one. + // Allocate a new var idx, and insert a new bound ty. + let var = BoundVar::from_usize(self.still_bound_vars.len()); + self.still_bound_vars.push(BoundVariableKind::Ty(old_bound.kind)); + let mapped = Ty::new_bound( + self.tcx, + rustc_type_ir::INNERMOST, + BoundTy { var, kind: old_bound.kind }, + ); + self.mapping.insert(old_bound.var, mapped.into()); + mapped + }; + + shift_vars(self.tcx, mapped, self.binder.as_u32()) + } else { + ty.super_fold_with(self) + } + } + + fn fold_region(&mut self, re: Region<'db>) -> Region<'db> { + if let RegionKind::ReBound(BoundVarIndexKind::Bound(binder), old_bound) = re.kind() + && self.binder == binder + { + let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) { + mapped.expect_region() + } else { + let var = BoundVar::from_usize(self.still_bound_vars.len()); + self.still_bound_vars.push(BoundVariableKind::Region(old_bound.kind)); + let mapped = Region::new_bound( + self.tcx, + rustc_type_ir::INNERMOST, + BoundRegion { var, kind: old_bound.kind }, + ); + self.mapping.insert(old_bound.var, mapped.into()); + mapped + }; + + shift_vars(self.tcx, mapped, self.binder.as_u32()) + } else { + re + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + if !ct.has_bound_vars() { + return ct; + } + + if let ConstKind::Bound(BoundVarIndexKind::Bound(binder), old_bound) = ct.kind() + && self.binder == binder + { + let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) { + mapped.expect_const() + } else { + let var = BoundVar::from_usize(self.still_bound_vars.len()); + self.still_bound_vars.push(BoundVariableKind::Const); + let mapped = + Const::new_bound(self.tcx, rustc_type_ir::INNERMOST, BoundConst::new(var)); + self.mapping.insert(old_bound.var, mapped.into()); + mapped + }; + + shift_vars(self.tcx, mapped, self.binder.as_u32()) + } else { + ct.super_fold_with(self) + } + } + + fn fold_predicate(&mut self, p: Predicate<'db>) -> Predicate<'db> { + if !p.has_bound_vars() { p } else { p.super_fold_with(self) } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash, SalsaValue)] pub struct GenericPredicates { // The order is the following: // // 1. If `has_trait_implied_predicate == true`, the implicit trait predicate. - // 2. The bounds of the associated types of the parents, coming from `Trait`. + // 2. The explicit, self-only predicates for the parent. + // 3. The bounds of the associated types of the parents, coming from `Trait`. // Note: associated type bounds from `Self::Assoc: Trait` on traits *won't* be included // here, they are in 3. - // 3. The explicit, self-only predicates for the parent. // 4. The explicit, self-only trait predicate for the child, // 5. The bounds of the associated types of the child. predicates: StoredEarlyBinder, // Keep this ordered according to the above. has_trait_implied_predicate: bool, - parent_explicit_self_predicates_start: u32, + parent_assoc_ty_bounds_start: u32, own_predicates_start: u32, own_assoc_ty_bounds_start: u32, } @@ -2386,9 +2642,9 @@ impl GenericPredicates { pub fn empty() -> &'static GenericPredicates { static EMPTY: OnceLock = OnceLock::new(); EMPTY.get_or_init(|| GenericPredicates { - predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&[]).store()), + predicates: StoredEarlyBinder::bind(Clauses::default().store()), has_trait_implied_predicate: false, - parent_explicit_self_predicates_start: 0, + parent_assoc_ty_bounds_start: 0, own_predicates_start: 0, own_assoc_ty_bounds_start: 0, }) @@ -2402,7 +2658,7 @@ impl GenericPredicates { Self { predicates, has_trait_implied_predicate: false, - parent_explicit_self_predicates_start: 0, + parent_assoc_ty_bounds_start: 0, own_predicates_start: 0, own_assoc_ty_bounds_start: len, } @@ -2461,10 +2717,16 @@ impl GenericPredicates { &self, ) -> EarlyBinder<'_, impl Iterator>> { self.predicates.get().map_bound(|it| { - it.as_slice()[self.parent_explicit_self_predicates_start as usize - ..self.own_assoc_ty_bounds_start as usize] - .iter() - .copied() + std::iter::chain( + it.as_slice()[usize::from(self.has_trait_implied_predicate) + ..self.parent_assoc_ty_bounds_start as usize] + .iter() + .copied(), + it.as_slice() + [self.own_predicates_start as usize..self.own_assoc_ty_bounds_start as usize] + .iter() + .copied(), + ) }) } @@ -2472,13 +2734,14 @@ impl GenericPredicates { pub fn explicit_assoc_types_predicates( &self, ) -> EarlyBinder<'_, impl Iterator>> { - self.predicates.get().map_bound(|predicates| { - let predicates = predicates.as_slice(); - predicates[usize::from(self.has_trait_implied_predicate) - ..self.parent_explicit_self_predicates_start as usize] - .iter() - .copied() - .chain(predicates[self.own_assoc_ty_bounds_start as usize..].iter().copied()) + self.predicates.get().map_bound(|it| { + std::iter::chain( + it.as_slice()[self.parent_assoc_ty_bounds_start as usize + ..self.own_predicates_start as usize] + .iter() + .copied(), + it.as_slice()[self.own_assoc_ty_bounds_start as usize..].iter().copied(), + ) }) } } @@ -2491,7 +2754,7 @@ pub(crate) fn param_env_from_predicates<'db>( interner, predicates.all_predicates().iter_identity().map(Unnormalized::skip_norm_wip), ); - let clauses = Clauses::new_from_iter(interner, clauses); + let clauses = Clauses::new_from_iter(clauses); // FIXME: We should normalize projections here, like rustc does. ParamEnv { clauses } @@ -2536,124 +2799,92 @@ fn generic_predicates<'db>( ); let generics = generics.get().unwrap(); let sized_trait = ctx.lang_items.Sized; + let myself = generics.owner(); - // We need to lower parents and self separately - see the comment below lowering of implicit - // `Sized` predicates for why. let mut own_predicates = Vec::new(); - let mut parent_predicates = Vec::new(); let mut own_assoc_ty_bounds = Vec::new(); - let mut parent_assoc_ty_bounds = Vec::new(); let own_implicit_trait_predicate = implicit_trait_predicate(interner, def); - let parent_implicit_trait_predicate = if let Some(parent) = generics.parent() { - implicit_trait_predicate(interner, parent.def()) + let parent = if let Some(parent) = generics.parent() { + &GenericPredicates::query_with_diagnostics(db, parent.def()).value } else { - None + GenericPredicates::empty() }; - for maybe_parent_generics in generics.iter_owners() { - // Collect only diagnostics from the child, not including parents. - ctx.diagnostics.clear(); - ctx.set_owner(maybe_parent_generics); - for pred in maybe_parent_generics.where_predicates() { - tracing::debug!(?pred); - for (pred, source) in ctx.lower_where_predicate(pred, false) { - match source { - GenericPredicateSource::SelfOnly => { - if maybe_parent_generics.def() == def { - own_predicates.push(pred); - } else { - parent_predicates.push(pred); - } - } - GenericPredicateSource::AssocTyBound => { - if maybe_parent_generics.def() == def { - own_assoc_ty_bounds.push(pred); - } else { - parent_assoc_ty_bounds.push(pred); - } - } - } - } - } + let generics = myself; - if maybe_parent_generics.def() == def { - push_const_arg_has_type_predicates(db, &mut own_predicates, maybe_parent_generics); - } else { - push_const_arg_has_type_predicates(db, &mut parent_predicates, maybe_parent_generics); + for pred in generics.where_predicates() { + tracing::debug!(?pred); + for (pred, source) in ctx.lower_where_predicate(pred, false) { + match source { + GenericPredicateSource::SelfOnly => own_predicates.push(pred), + GenericPredicateSource::AssocTyBound => own_assoc_ty_bounds.push(pred), + } } + } - if let Some(sized_trait) = sized_trait { - let mut add_sized_clause = |param_idx, param_id, param_data| { - let ( - GenericParamId::TypeParamId(param_id), - GenericParamDataRef::TypeParamData(param_data), - ) = (param_id, param_data) - else { - return; - }; - - if param_data.provenance == TypeParamProvenance::TraitSelf { - return; - } + push_const_arg_has_type_predicates(db, &mut own_predicates, generics); - let param_ty = Ty::new_param(interner, param_id, param_idx); - if ctx.unsized_types.contains(¶m_ty) { - return; - } - let trait_ref = TraitRef::new_from_args( - interner, - sized_trait.into(), - GenericArgs::new_from_slice(&[param_ty.into()]), - ); - let clause = Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - )); - if maybe_parent_generics.def() == def { - own_predicates.push(clause); - } else { - parent_predicates.push(clause); - } + if let Some(sized_trait) = sized_trait { + let mut add_sized_clause = |param_idx, param_id, param_data| { + let ( + GenericParamId::TypeParamId(param_id), + GenericParamDataRef::TypeParamData(param_data), + ) = (param_id, param_data) + else { + return; }; - maybe_parent_generics.iter_with_idx().for_each(|(param_idx, param_id, param_data)| { - add_sized_clause(param_idx, param_id, param_data); - }); - } - // We do not clear `ctx.unsized_types`, as the `?Sized` clause of a child (e.g. an associated type) can - // be declared on the parent (e.g. the trait). It is nevertheless fine to register the implicit `Sized` - // predicates before lowering the child, as a child cannot define a `?Sized` predicate for its parent. - // But we do have to lower the parent first. + if param_data.provenance == TypeParamProvenance::TraitSelf { + return; + } + + let param_ty = Ty::new_param(interner, param_id, param_idx); + if ctx.unsized_types.contains(¶m_ty) { + return; + } + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_slice(&[param_ty.into()]), + ); + let clause = Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )); + own_predicates.push(clause); + }; + generics.iter_with_idx().for_each(|(param_idx, param_id, param_data)| { + add_sized_clause(param_idx, param_id, param_data); + }); } let diagnostics = mem::take(&mut ctx.diagnostics); let defined_anon_consts = mem::take(&mut ctx.defined_anon_consts); - let predicates = parent_implicit_trait_predicate + let parent_predicates = parent.predicates.get().skip_binder().as_slice(); + let predicates = parent_predicates .iter() .chain(own_implicit_trait_predicate.iter()) - .chain(parent_assoc_ty_bounds.iter()) - .chain(parent_predicates.iter()) .chain(own_predicates.iter()) .chain(own_assoc_ty_bounds.iter()) .copied() .collect::>(); let has_trait_implied_predicate = - parent_implicit_trait_predicate.is_some() || own_implicit_trait_predicate.is_some(); - let parent_explicit_self_predicates_start = - has_trait_implied_predicate as u32 + parent_assoc_ty_bounds.len() as u32; + parent.has_trait_implied_predicate || own_implicit_trait_predicate.is_some(); + let parent_assoc_ty_bounds_start = + parent.own_assoc_ty_bounds_start + u32::from(own_implicit_trait_predicate.is_some()); let own_predicates_start = - parent_explicit_self_predicates_start + parent_predicates.len() as u32; + parent_predicates.len() as u32 + u32::from(own_implicit_trait_predicate.is_some()); let own_assoc_ty_bounds_start = own_predicates_start + own_predicates.len() as u32; let predicates = GenericPredicates { has_trait_implied_predicate, - parent_explicit_self_predicates_start, + parent_assoc_ty_bounds_start, own_predicates_start, own_assoc_ty_bounds_start, predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()), @@ -2687,19 +2918,23 @@ fn generic_predicates<'db>( fn push_const_arg_has_type_predicates<'db>( db: &'db dyn HirDatabase, predicates: &mut Vec>, - single_generics: &SingleGenerics<'db>, + generics: &SingleGenerics<'db>, ) { + let mut const_param_tys = None; let interner = DbInterner::new_no_crate(db); - for (param_index, param_id, _) in single_generics.iter_with_idx() { - let GenericParamId::ConstParamId(param_id) = param_id else { continue }; + generics.iter_with_idx().for_each(|(param_index, param_id, _)| { + let GenericParamId::ConstParamId(param_id) = param_id else { return }; + + let const_param_tys = + *const_param_tys.get_or_insert_with(|| const_param_types(db, generics.def())); predicates.push(Clause( ClauseKind::ConstArgHasType( Const::new_param(interner, ParamConst { id: param_id, index: param_index }), - db.const_param_ty(param_id), + const_param_tys.get(param_id.local_id()), ) .upcast(interner), )); - } + }); } #[derive(Debug, Clone, PartialEq, Eq, Hash, SalsaValue)] @@ -2710,6 +2945,11 @@ impl GenericDefaults { pub fn as_ref(&self) -> GenericDefaultsRef<'_> { GenericDefaultsRef(&self.0) } + + fn push(&mut self, index: usize, default: StoredEarlyBinder) { + self.0.extend((0..index - self.0.len()).map(|_| None)); + self.0.push(Some(default)); + } } #[derive(Debug, Clone, Copy)] @@ -2754,21 +2994,19 @@ pub(crate) fn generic_defaults_with_diagnostics<'db>( ) .with_impl_trait_mode(ImplTraitLoweringMode::Disallowed); let generics = generics.get().unwrap(); - let mut defaults = ThinVec::new(); - if let Some(parent) = generics.parent() { - ctx.set_owner(parent); - defaults.extend( - parent.iter_with_idx().map(|(idx, _id, p)| handle_generic_param(&mut ctx, idx, p)), - ); - } - ctx.diagnostics.clear(); // Don't include diagnostics from the parent. - ctx.defined_anon_consts.clear(); - ctx.set_owner(generics.owner()); - defaults.extend( - generics.iter_self_with_idx().map(|(idx, _id, p)| handle_generic_param(&mut ctx, idx, p)), - ); - defaults.shrink_to_fit(); - return TyLoweringResult::from_ctx(GenericDefaults(defaults), ctx); + + let mut defaults = if let Some(parent) = generics.parent() { + generic_defaults_with_diagnostics(db, parent.def()).value.clone() + } else { + GenericDefaults(ThinVec::new()) + }; + + generics + .iter_self_with_idx() + .filter_map(|(idx, _id, p)| Some((idx as usize, handle_generic_param(&mut ctx, idx, p)?))) + .for_each(|(idx, default)| defaults.push(idx, default)); + defaults.0.shrink_to_fit(); + return TyLoweringResult::from_ctx(defaults, ctx); fn handle_generic_param<'db>( ctx: &mut TyLoweringContext<'db, '_>, @@ -2784,7 +3022,7 @@ pub(crate) fn generic_defaults_with_diagnostics<'db>( GenericParamDataRef::ConstParamData(p) => { let val = p.default.map(|c| { let param_ty = ctx.lower_ty(p.ty); - let c = ctx.lower_const(c, param_ty); + let c = ctx.lower_const(c, param_ty, false); GenericArg::from(c).store() }); val.map(StoredEarlyBinder::bind) @@ -2852,11 +3090,11 @@ pub(crate) fn fn_sig_for_fn<'db>( }; let impl_traits = ctx_ret.take_defined_opaques(); - let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret))); + let inputs_and_output = Tys::new_from_iter(params.chain(Some(ret))); ctx_params.diagnostics.extend(ctx_ret.diagnostics); ctx_params.defined_anon_consts.extend(ctx_ret.defined_anon_consts); - let binder = TyLoweringContext::bound_vars(db, interner, def.into(), &generics); + let binder = TyLoweringContext::bound_vars(db, def.into(), &generics); let result = StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::bind_with_vars( FnSig { inputs_and_output, @@ -2887,8 +3125,7 @@ fn ctor_signature( let params = field_tys.iter().map(|(_, field)| field.ty().skip_binder()); let ret = type_for_adt(db, adt).skip_binder(); - let inputs_and_output = - Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret))); + let inputs_and_output = Tys::new_from_iter(params.chain(Some(ret))); StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig { fn_sig_kind: FnSigKind::new(ExternAbi::Rust, Safety::Safe, false), inputs_and_output, @@ -2911,85 +3148,6 @@ fn fn_sig_for_enum_variant_constructor( ctor_signature(db, def.into(), def.lookup(db).parent.into()) } -// FIXME: Remove this. -pub(crate) fn associated_ty_item_bounds<'db>( - db: &'db dyn HirDatabase, - type_alias: TypeAliasId, -) -> EarlyBinder<'db, BoundExistentialPredicates<'db>> { - let type_alias_data = TypeAliasSignature::of(db, type_alias); - let resolver = type_alias.resolver(db); - let interner = DbInterner::new_no_crate(db); - let generics = OnceCell::new(); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &type_alias_data.store, - ExpressionStoreOwnerId::Signature(type_alias.into()), - type_alias.into(), - &generics, - LifetimeElisionKind::AnonymousReportError, - LifetimeLoweringMode::Bound, - ); - // FIXME: we should never create non-existential predicates in the first place - // For now, use an error type so we don't run into dummy binder issues - let self_ty = Ty::new_error(interner, ErrorGuaranteed); - - let mut bounds = Vec::new(); - for bound in &type_alias_data.bounds { - ctx.lower_type_bound(bound, self_ty, false).for_each(|(pred, _)| { - if let Some(bound) = pred - .kind() - .map_bound(|c| match c { - rustc_type_ir::ClauseKind::Trait(t) => { - let id = t.def_id(); - let is_auto = TraitSignature::of(db, id.0).flags.contains(TraitFlags::AUTO); - if is_auto { - Some(ExistentialPredicate::AutoTrait(t.def_id())) - } else { - Some(ExistentialPredicate::Trait(ExistentialTraitRef::new_from_args( - interner, - t.def_id(), - GenericArgs::new_from_slice(&t.trait_ref.args[1..]), - ))) - } - } - rustc_type_ir::ClauseKind::Projection(p) => Some( - ExistentialPredicate::Projection(ExistentialProjection::new_from_args( - interner, - p.def_id(), - GenericArgs::new_from_slice(&p.projection_term.args[1..]), - p.term, - )), - ), - rustc_type_ir::ClauseKind::TypeOutlives(_) => None, - rustc_type_ir::ClauseKind::RegionOutlives(_) - | rustc_type_ir::ClauseKind::ConstArgHasType(_, _) - | rustc_type_ir::ClauseKind::WellFormed(_) - | rustc_type_ir::ClauseKind::ConstEvaluatable(_) - | rustc_type_ir::ClauseKind::HostEffect(_) - | rustc_type_ir::ClauseKind::UnstableFeature(_) => unreachable!(), - }) - .transpose() - { - bounds.push(bound); - } - }); - } - - if !ctx.unsized_types.contains(&self_ty) - && let Some(sized_trait) = ctx.lang_items.Sized - { - let sized_clause = Binder::dummy(ExistentialPredicate::Trait(ExistentialTraitRef::new( - interner, - sized_trait.into(), - [] as [GenericArg<'_>; 0], - ))); - bounds.push(sized_clause); - } - - EarlyBinder::bind(BoundExistentialPredicates::new_from_slice(&bounds)) -} - pub(crate) fn associated_type_by_name_including_super_traits_allow_ambiguity<'db>( db: &'db dyn HirDatabase, trait_ref: TraitRef<'db>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index 7e55ef2963169..4d8bca134d548 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -214,7 +214,6 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { span, ); let args = GenericArgs::new_from_iter( - self.ctx.interner, trait_ref .args .iter() @@ -544,7 +543,6 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { let substs = self.substs_from_path_segment(assoc_type.into(), infer_args, None, true, span); let substs = GenericArgs::new_from_iter( - interner, trait_args.iter().chain(substs.iter().skip(trait_args.len())), ); @@ -745,7 +743,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }; self.ctx .ctx - .lower_const(konst, const_param_ty(self.ctx.ctx.db, const_id)) + .lower_const(konst, const_param_ty(self.ctx.ctx.db, const_id), false) .into() } _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), @@ -928,7 +926,6 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { ) }); let args = GenericArgs::new_from_iter( - interner, super_trait_args.iter().chain(args.iter().skip(super_trait_args.len())), ); let projection_term = AliasTerm::new_from_args( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 37e29b10cd5ad..2db1a856c7083 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -450,10 +450,7 @@ pub(crate) fn lookup_impl_method_query<'db>( ( impl_fn, - GenericArgs::new_from_iter( - interner, - impl_subst.iter().chain(fn_subst.iter().skip(trait_params)), - ), + GenericArgs::new_from_iter(impl_subst.iter().chain(fn_subst.iter().skip(trait_params))), ) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs index 5d10a0b21e46b..6c79677e4623a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs @@ -404,7 +404,7 @@ impl<'a, 'db> ConfirmContext<'a, 'db> { unreachable!("non-const param ID for const param"); }; let const_ty = self.ctx.db.const_param_ty(const_id); - self.ctx.create_anon_const(konst.expr, const_ty, false).into() + self.ctx.create_anon_const(konst.expr, const_ty).into() } _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 433fc274ea955..0566eedd33dcb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -6,13 +6,12 @@ use hir_def::{ FieldId, LocalFieldId, StaticId, UnionId, VariantId, hir::{BindingId, Expr, ExprId, Ordering, PatId}, }; -use intern::{InternedSlice, InternedSliceRef, impl_slice_internable}; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; use macros::{TypeFoldable, TypeVisitable}; use rustc_ast_ir::Mutability; use rustc_hash::FxHashMap; use rustc_type_ir::{ - CollectAndApply, GenericTypeVisitable, + GenericTypeVisitable, inherent::{GenericArgs as _, IntoKind, Ty as _}, }; use salsa::SalsaValue; @@ -26,8 +25,8 @@ use crate::{ next_solver::{ Allocation, AllocationData, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredAllocation, StoredConst, StoredGenericArgs, StoredTy, Ty, TyKind, - impl_stored_interned_slice, infer::{InferCtxt, traits::ObligationCause}, + interned_slice, obligation_ctxt::ObligationCtxt, }, }; @@ -220,52 +219,14 @@ impl GenericTypeVisitable for PlaceElem fn generic_visit_with(&self, _: &mut W) {} } -impl_slice_internable!(gc; ProjectionStorage, (), PlaceElem); -impl_stored_interned_slice!(ProjectionStorage, Projection, StoredProjection); - -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct Projection<'db> { - interned: InternedSliceRef<'db, ProjectionStorage>, -} - -impl<'db> std::fmt::Debug for Projection<'db> { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - (*self).as_slice().fmt(fmt) - } -} +interned_slice!(ProjectionStorage, Projection, StoredProjection, projection, PlaceElem, PlaceElem); impl<'db> Projection<'db> { - pub fn new_from_iter(args: I) -> T::Output - where - I: IntoIterator, - T: CollectAndApply, - { - CollectAndApply::collect_and_apply(args.into_iter(), Self::new_from_slice) - } - - #[inline] - pub fn new_from_slice(slice: &[PlaceElem]) -> Self { - Self { interned: InternedSlice::from_header_and_slice((), slice) } - } - - #[inline] - pub fn as_slice(self) -> &'db [PlaceElem] { - &self.interned.get().slice - } - pub fn project(self, projection: PlaceElem) -> Projection<'db> { Projection::new_from_iter(self.as_slice().iter().copied().chain([projection])) } } -impl<'db> std::ops::Deref for Projection<'db> { - type Target = [PlaceElem]; - - fn deref(&self) -> &Self::Target { - self.as_slice() - } -} - impl StoredProjection { pub fn as_slice(&self) -> &[PlaceElem] { self.as_ref().as_slice() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 37f3e27631810..57174d4b13363 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -1260,7 +1260,9 @@ impl<'a, 'db> Evaluator<'a, 'db> { ty = z; let size = if ty.is_str() { if *op != BinOp::Eq { - never!("Only eq is builtin for `str`"); + return Err(MirEvalError::NotSupported( + "Only eq is builtin for `str`".to_owned(), + )); } let ls = from_bytes!(usize, &lc[self.ptr_size()..self.ptr_size() * 2]); let rs = from_bytes!(usize, &rc[self.ptr_size()..self.ptr_size() * 2]); @@ -1679,7 +1681,9 @@ impl<'a, 'db> Evaluator<'a, 'db> { return Ok(0); }; match &layout.variants { - Variants::Empty => unreachable!(), + Variants::Empty => Err(MirEvalError::UndefinedBehavior( + "reading the discriminant of an uninhabited enum".to_owned(), + )), Variants::Single { index } => { let r = self.const_eval_discriminant(e.enum_variants(self.db).variants[index.0].0)?; @@ -2900,7 +2904,6 @@ impl<'a, 'db> Evaluator<'a, 'db> { ty, }; let generics_for_target = GenericArgs::new_from_iter( - self.interner(), generic_args .iter() .enumerate() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 3b82ed6f798e4..2b2d72e588646 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -2,9 +2,10 @@ use std::{fmt::Write, iter, mem}; +use arrayvec::ArrayVec; use base_db::Crate; use hir_def::{ - DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GenericParamId, HasModule, + AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GenericParamId, HasModule, ItemContainerId, LocalFieldId, Lookup, TraitId, expr_store::{Body, ExpressionStore, HygieneId, body::Param, path::Path}, hir::{ @@ -966,23 +967,62 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.lower_expr_to_place(id, place, current) } Expr::Cast { expr, type_ref: _ } => { - let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)? else { - return Ok(None); - }; // Since we don't have THIR, this is the "zipped" version of [rustc's HIR lowering](https://github.com/rust-lang/rust/blob/e71f9529121ca8f687e4b725e3c9adc3f1ebab4d/compiler/rustc_mir_build/src/thir/cx/expr.rs#L165-L178) // and [THIR lowering as RValue](https://github.com/rust-lang/rust/blob/a4601859ae3875732797873612d424976d9e3dd0/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs#L193-L313) - let rvalue = if self.infer.coercion_casts.contains(expr) { - Rvalue::Use(it) + let (rvalue, current) = if self.infer.coercion_casts.contains(expr) { + let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)? + else { + return Ok(None); + }; + (Rvalue::Use(it), current) } else { let source_ty = self.infer.expr_ty(*expr); let target_ty = self.infer.expr_ty(expr_id); + let (it, source_ty, current) = + if let Some(VariantId::EnumVariantId(variant_id)) = + self.infer.variant_resolution_for_expr(*expr) + { + self.lower_variant_discriminant(current, variant_id)? + } else if let TyKind::Adt(adt, _) = source_ty.kind() + && adt.is_enum() + { + let Some((enum_place, current)) = + self.lower_expr_as_place(current, *expr, true)? + else { + return Ok(None); + }; + let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128); + let discr_place: Place<'db> = + self.temp(discr_ty, current, expr_id.into())?.into(); + + self.push_assignment( + current, + discr_place, + Rvalue::Discriminant(enum_place.store()), + expr_id.into(), + ); + ( + Operand { + kind: OperandKind::Copy(discr_place.store()), + span: Some(expr_id.into()), + }, + discr_ty, + current, + ) + } else { + let Some((it, current)) = + self.lower_expr_to_some_operand(*expr, current)? + else { + return Ok(None); + }; + (it, source_ty, current) + }; let cast_kind = if source_ty.as_reference().is_some() { CastKind::PointerCoercion(PointerCast::ArrayToPointer) } else { cast_kind(self.db, source_ty, target_ty)? }; - - Rvalue::Cast(cast_kind, it, target_ty.store()) + (Rvalue::Cast(cast_kind, it, target_ty.store()), current) }; self.push_assignment(current, place, rvalue, expr_id.into()); Ok(Some(current)) @@ -1519,6 +1559,17 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { Ok(prev_block) } + fn lower_variant_discriminant( + &mut self, + current: BasicBlockId, + variant_id: EnumVariantId, + ) -> Result<'db, (Operand, Ty<'db>, BasicBlockId)> { + let discriminant = self.const_eval_discriminant(variant_id)?; + let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128); + let operand = Operand::from_bytes(Box::new(discriminant.to_le_bytes()), discr_ty); + Ok((operand, discr_ty, current)) + } + fn lower_call_and_args( &mut self, func: Operand, @@ -2012,20 +2063,31 @@ fn convert_closure_capture_projections( _db: &dyn HirDatabase, place: &HirPlace, ) -> impl Iterator { - place.projections.iter().enumerate().map(|(i, proj)| match proj.kind { - HirProjectionKind::Deref => ProjectionElem::Deref, - HirProjectionKind::Field { field_idx, variant_idx: _ } => { - let ty = place.ty_before_projection(i); - match ty.kind() { - TyKind::Tuple(_) => ProjectionElem::Field(FieldIndex(field_idx)), - TyKind::Adt(_, _) => { - let local_field_id = LocalFieldId::from_raw(RawIdx::from_u32(field_idx)); - ProjectionElem::Field(local_field_id.into()) + place.projections.iter().enumerate().flat_map(|(i, proj)| { + let mut projections = ArrayVec::::new(); + match proj.kind { + HirProjectionKind::Deref => projections.push(ProjectionElem::Deref), + HirProjectionKind::Field { field_idx, variant_idx } => { + let ty = place.ty_before_projection(i); + match ty.kind() { + TyKind::Tuple(_) => { + projections.push(ProjectionElem::Field(FieldIndex(field_idx))) + } + TyKind::Adt(adt_def, _) => { + if let AdtId::EnumId(enum_id) = adt_def.def_id() { + let variant = + enum_id.enum_variants(_db).variants[variant_idx as usize].0.into(); + projections.push(ProjectionElem::Downcast(variant)); + } + let local_field_id = LocalFieldId::from_raw(RawIdx::from_u32(field_idx)); + projections.push(ProjectionElem::Field(local_field_id.into())); + } + _ => panic!("unexpected type"), } - _ => panic!("unexpected type"), } + _ => panic!("unexpected projection"), } - _ => panic!("unexpected projection"), + projections.into_iter() }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 6263571286721..24122da4c981b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -165,79 +165,79 @@ pub fn default_types<'db>() -> &'db DefaultAny<'db> { ty.as_ref() }; let create_generic_args = |slice| { - let ty = GenericArgs::new_from_slice(slice); + let ty = GenericArgs::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_bound_var_kinds = |slice| { - let ty = BoundVarKinds::new_from_slice(slice); + let ty = BoundVarKinds::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_canonical_vars = |slice| { - let ty = CanonicalVarKinds::new_from_slice(slice); + let ty = CanonicalVarKinds::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_variances_of = |slice| { - let ty = VariancesOf::new_from_slice(slice); + let ty = VariancesOf::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_pat_list = |slice| { - let ty = PatList::new_from_slice(slice); + let ty = PatList::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_predefined_opaques = |slice| { - let ty = PredefinedOpaques::new_from_slice(slice); + let ty = PredefinedOpaques::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_solver_def_ids = |slice| { - let ty = SolverDefIds::new_from_slice(slice); + let ty = SolverDefIds::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_bound_existential_predicates = |slice| { - let ty = BoundExistentialPredicates::new_from_slice(slice); + let ty = BoundExistentialPredicates::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_clauses = |slice| { - let ty = Clauses::new_from_slice(slice); + let ty = Clauses::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_region_assumptions = |slice| { - let ty = RegionAssumptions::new_from_slice(slice); + let ty = RegionAssumptions::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_tys = |slice| { - let ty = Tys::new_from_slice(slice); + let ty = Tys::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_consts = |slice| { - let ty = Consts::new_from_slice(slice); + let ty = Consts::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; let create_projection = |slice| { - let it = crate::mir::Projection::new_from_slice(slice); + let it = crate::mir::Projection::new_from_slice_no_empty_check(slice); // We need to increase the refcount (forever), so that the types won't be freed. let it = ManuallyDrop::new(it.store()); it.as_ref() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs index 70db32e09b748..cc4318e6f32ae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs @@ -127,7 +127,6 @@ pub(super) fn allocation_to_const<'db>( }; let u8_values = &interner.default_types().consts.u8_values; ValTreeKind::Branch(Consts::new_from_iter( - interner, bytes.iter().map(|&byte| u8_values[usize::from(byte)]), )) } @@ -154,7 +153,7 @@ pub(super) fn allocation_to_const<'db>( let bytes = &bytes[offset..offset + size_one]; allocation_to_const(interner, ty, bytes, memory_map, param_env) }); - ValTreeKind::Branch(Consts::new_from_iter(interner, items)) + ValTreeKind::Branch(Consts::new_from_iter(items)) } TyKind::Dynamic(_, _) => { let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap()); @@ -215,7 +214,7 @@ pub(super) fn allocation_to_const<'db>( param_env, ) }); - ValTreeKind::Branch(Consts::new_from_iter(interner, items)) + ValTreeKind::Branch(Consts::new_from_iter(items)) } TyKind::Adt(..) => { // FIXME: This requires `adt_const_params`. @@ -249,7 +248,7 @@ pub(super) fn allocation_to_const<'db>( param_env, ) }); - ValTreeKind::Branch(Consts::new_from_iter(interner, items)) + ValTreeKind::Branch(Consts::new_from_iter(items)) } TyKind::Never => return Const::error(interner), // FIXME: diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 483811f9e6f0c..81d36c2bb037c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -623,17 +623,9 @@ impl<'db> rustc_type_ir::relate::Relate> for GenericArgs<'db> { a: Self, b: Self, ) -> rustc_type_ir::relate::RelateResult, Self> { - GenericArgs::new_from_iter( - relation.cx(), - std::iter::zip(a.iter(), b.iter()).map(|(a, b)| { - relation.relate_with_variance( - Variance::Invariant, - VarianceDiagInfo::default(), - a, - b, - ) - }), - ) + GenericArgs::new_from_iter(std::iter::zip(a.iter(), b.iter()).map(|(a, b)| { + relation.relate_with_variance(Variance::Invariant, VarianceDiagInfo::default(), a, b) + })) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs index 270bc73d5f53f..6eef2219b73e9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs @@ -348,10 +348,28 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { fn opaques_with_sub_unified_hidden_type( &self, - _ty: TyVid, + ty_vid: TyVid, ) -> Vec> { - // FIXME: I guess we are okay without this for now since currently r-a lacks of - // detailed checks over opaque types. Might need to implement this in future. - vec![] + let ty_sub_vid = self.sub_unification_table_root_var(ty_vid); + let inner = &mut *self.inner.borrow_mut(); + let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log); + inner + .opaque_type_storage + .iter_opaque_types() + .filter_map(|(key, hidden_ty)| { + if let TyKind::Infer(InferTy::TyVar(hidden_vid)) = hidden_ty.ty.kind() { + let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid); + if opaque_sub_vid == ty_sub_vid { + return Some(rustc_type_ir::AliasTy::new_from_args( + self.interner, + rustc_type_ir::Opaque { def_id: key.def_id }, + key.args, + )); + } + } + + None + }) + .collect() } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 7c419147d2143..f062c6fad560f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -1242,7 +1242,10 @@ impl<'db> InferCtxt<'db> { } } - fn sub_unification_table_root_var(&self, var: rustc_type_ir::TyVid) -> rustc_type_ir::TyVid { + pub(crate) fn sub_unification_table_root_var( + &self, + var: rustc_type_ir::TyVid, + ) -> rustc_type_ir::TyVid { self.inner.borrow_mut().type_variables().sub_unification_table_root_var(var) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index df7c9bfe1ec44..94f27469a7ba1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -96,12 +96,24 @@ macro_rules! interned_slice { #[inline] pub fn new_from_slice(slice: &[$ty_db]) -> Self { + if slice.is_empty() { + // Common case: avoid looking up the empty slice. + Self::empty() + } else { + Self::new_from_slice_no_empty_check(slice) + } + } + + /// Same as [`Self::new_from_slice()`] but won't use the global empty slice `slice.is_empty()`, because someone + /// needs to intern the global slice as well. + #[inline] + pub(crate) fn new_from_slice_no_empty_check(slice: &[$ty_db]) -> Self { let slice = unsafe { ::std::mem::transmute::<&[$ty_db], &[$ty_static]>(slice) }; Self { interned: ::intern::InternedSlice::from_header_and_slice((), slice) } } #[inline] - pub fn new_from_iter(_interner: DbInterner<'db>, args: I) -> T::Output + pub fn new_from_iter(args: I) -> T::Output where I: IntoIterator, T: ::rustc_type_ir::CollectAndApply<$ty_db, Self>, @@ -247,13 +259,13 @@ macro_rules! impl_foldable_for_interned_slice { self, folder: &mut F, ) -> Result { - Self::new_from_iter(folder.cx(), self.iter().map(|it| it.try_fold_with(folder))) + Self::new_from_iter(self.iter().map(|it| it.try_fold_with(folder))) } fn fold_with>>( self, folder: &mut F, ) -> Self { - Self::new_from_iter(folder.cx(), self.iter().map(|it| it.fold_with(folder))) + Self::new_from_iter(self.iter().map(|it| it.fold_with(folder))) } } }; @@ -847,7 +859,6 @@ impl<'db> rustc_type_ir::relate::Relate> for Pattern<'db> { return Err(TypeError::Mismatch); } let pats = PatList::new_from_iter( - relation.cx(), std::iter::zip(a.iter(), b.iter()).map(|(a, b)| relation.relate(a, b)), )?; Ok(Pattern::new(tcx, PatternKind::Or(pats))) @@ -1014,7 +1025,7 @@ impl<'db> Interner for DbInterner<'db> { I: Iterator, T: rustc_type_ir::CollectAndApply, { - GenericArgs::new_from_iter(self, args) + GenericArgs::new_from_iter(args) } type UnsizingParams = UnsizingParams; @@ -1053,7 +1064,8 @@ impl<'db> Interner for DbInterner<'db> { } fn assert_evaluation_is_concurrent(&self) { - panic!("evaluation shouldn't be concurrent yet") + // Turns out, the assumption for this function isn't perfect. + // See https://github.com/rust-lang/trait-system-refactor-initiative/issues/234 } fn expand_abstract_consts>(self, _: T) -> T { @@ -1075,7 +1087,6 @@ impl<'db> Interner for DbInterner<'db> { // We compute them based on the only `Ty` level info in rustc, // move `variances_of_opaque` into `rustc_next_trait_solver` for reuse. return VariancesOf::new_from_iter( - self, (0..self.generics_of(def_id).count()).map(|_| Variance::Invariant), ); } @@ -1161,12 +1172,9 @@ impl<'db> Interner for DbInterner<'db> { def_id: Self::TraitAssocTermId, args: Self::GenericArgs, ) -> (rustc_type_ir::TraitRef, Self::GenericArgsSlice) { - let trait_def_id = self.projection_parent(def_id).0; - let trait_generics = crate::generics::generics(self.db, trait_def_id.into()); - let trait_generics_len = trait_generics.len(true); - let trait_args = GenericArgs::new_from_slice(&args.as_slice()[..trait_generics_len]); - let alias_args = &args.as_slice()[trait_generics_len..]; - (TraitRef::new_from_args(self, trait_def_id.into(), trait_args), alias_args) + let trait_def_id = self.projection_parent(def_id); + let trait_ref = TraitRef::from_assoc(self, trait_def_id, args); + (trait_ref, &args.as_slice()[trait_ref.args.len()..]) } fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool { @@ -1196,7 +1204,7 @@ impl<'db> Interner for DbInterner<'db> { I: Iterator, T: rustc_type_ir::CollectAndApply, { - Tys::new_from_iter(self, args) + Tys::new_from_iter(args) } fn projection_parent(self, def_id: Self::TraitAssocTermId) -> Self::TraitId { @@ -1347,10 +1355,7 @@ impl<'db> Interner for DbInterner<'db> { if all_bounds.len() == own_bounds.len() { EarlyBinder::bind(Clauses::empty()) } else { - EarlyBinder::bind(Clauses::new_from_iter( - self, - all_bounds.difference(&own_bounds).cloned(), - )) + EarlyBinder::bind(Clauses::new_from_iter(all_bounds.difference(&own_bounds).cloned())) } } @@ -1978,7 +1983,7 @@ impl<'db> Interner for DbInterner<'db> { let mut map = Default::default(); let delegate = Anonymize { interner: self, map: &mut map }; let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate); - let bound_vars = BoundVarKinds::new_from_iter(self, map.into_values()); + let bound_vars = BoundVarKinds::new_from_iter(map.into_values()); Binder::bind_with_vars(inner, bound_vars) } @@ -2292,7 +2297,6 @@ impl<'db> DbInterner<'db> { { FnSig { inputs_and_output: Tys::new_from_iter( - self, inputs.into_iter().chain(std::iter::once(output)), ), fn_sig_kind: FnSigKind::new(abi, safety, c_variadic), @@ -2484,7 +2488,7 @@ mod tls_db { #[inline] fn with(&self, op: impl FnOnce(&dyn HirDatabase) -> R) -> R { - let db = self.database.get().expect("Try to use attached db, but not db is attached"); + let db = self.database.get().expect("Tried to use attached db, but no db is attached"); // SAFETY: The db is attached, so it must be valid. op(unsafe { db.as_ref() }) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index a1e249eaf9517..460e266999edb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -136,8 +136,6 @@ impl<'db> rustc_type_ir::relate::Relate> for BoundExistentialPre a: Self, b: Self, ) -> rustc_type_ir::relate::RelateResult, Self> { - let interner = relation.cx(); - // We need to perform this deduplication as we sometimes generate duplicate projections in `a`. let mut a_v: Vec<_> = a.into_iter().collect(); let mut b_v: Vec<_> = b.into_iter().collect(); @@ -180,7 +178,7 @@ impl<'db> rustc_type_ir::relate::Relate> for BoundExistentialPre }, ); - BoundExistentialPredicates::new_from_iter(interner, v) + BoundExistentialPredicates::new_from_iter(v) } } @@ -280,6 +278,18 @@ impl<'db> Clauses<'db> { #[inline] pub fn new_from_slice(slice: &[Clause<'db>]) -> Self { + if slice.is_empty() { + // Common case: avoid looking up the empty slice. + Self::empty() + } else { + Self::new_from_slice_no_empty_check(slice) + } + } + + /// Same as [`Self::new_from_slice()`] but won't use the global empty slice `slice.is_empty()`, because someone + /// needs to intern the global slice as well. + #[inline] + pub(crate) fn new_from_slice_no_empty_check(slice: &[Clause<'db>]) -> Self { let slice = unsafe { ::std::mem::transmute::<&[Clause<'db>], &[Clause<'static>]>(slice) }; let flags = FlagComputation::>::for_clauses(slice); let flags = ClausesCachedTypeInfo(WithCachedTypeInfo { @@ -291,7 +301,7 @@ impl<'db> Clauses<'db> { } #[inline] - pub fn new_from_iter(_interner: DbInterner<'db>, args: I) -> T::Output + pub fn new_from_iter(args: I) -> T::Output where I: IntoIterator, T: CollectAndApply, Self>, @@ -369,14 +379,14 @@ impl<'db> rustc_type_ir::TypeSuperFoldable> for Clauses<'db> { self, folder: &mut F, ) -> Result { - Clauses::new_from_iter(folder.cx(), self.iter().map(|clause| clause.try_fold_with(folder))) + Clauses::new_from_iter(self.iter().map(|clause| clause.try_fold_with(folder))) } fn super_fold_with>>( self, folder: &mut F, ) -> Self { - Clauses::new_from_iter(folder.cx(), self.iter().map(|clause| clause.fold_with(folder))) + Clauses::new_from_iter(self.iter().map(|clause| clause.fold_with(folder))) } } @@ -891,7 +901,7 @@ impl<'db> rustc_type_ir::inherent::Clause> for Clause<'db> { .skip_norm_wip(); // 3) ['x] + ['b] -> ['x, 'b] let bound_vars = - BoundVarKinds::new_from_iter(cx, trait_bound_vars.iter().chain(pred_bound_vars.iter())); + BoundVarKinds::new_from_iter(trait_bound_vars.iter().chain(pred_bound_vars.iter())); let predicate: Predicate<'db> = ty::Binder::bind_with_vars(PredicateKind::Clause(new), bound_vars).upcast(cx); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index c1810bc659c01..84f21c96c9b05 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -610,7 +610,6 @@ impl<'db> Ty<'db> { ); FnSig { inputs_and_output: Tys::new_from_iter( - interner, sig.tupled_inputs_ty .tuple_fields() .iter() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs index 9a31443b25af8..37c79257e3af9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs @@ -89,7 +89,8 @@ fn representability_adt_ty<'db>( fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> { let generics = GenericParams::of(db, def_id.into()); - let mut params_in_repr = (0..generics.len_lifetimes() + generics.len_type_or_consts()) + let mut params_in_repr = (0..generics.len_early_bound_lifetimes() + + generics.len_type_or_consts()) .map(|_| false) .collect::>(); let mut handle_variant = |variant| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs index 7664d87cdee3e..92ffa472548da 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs @@ -1,7 +1,7 @@ //! Stuff for handling `#[target_feature]` (needed for unsafe check). -use std::borrow::Cow; use std::sync::LazyLock; +use std::{borrow::Cow, ops::Deref}; use hir_def::FunctionId; use hir_def::attrs::AttrFlags; @@ -51,7 +51,9 @@ impl<'db> TargetFeatures<'db> { static TARGET_FEATURE_IMPLICATIONS: LazyLock>> = LazyLock::new(|| { let mut result = FxHashMap::>::default(); - for &(feature_str, implications) in TARGET_FEATURE_IMPLICATIONS_RAW { + for &(feature_str, ref _stability, implications) in + TARGET_FEATURE_IMPLICATIONS_RAW.iter().flat_map(Deref::deref) + { let feature = Symbol::intern(feature_str); let implications = implications.iter().copied().map(Symbol::intern); // Some target features appear in two archs, e.g. Arm and x86. @@ -67,187 +69,987 @@ static TARGET_FEATURE_IMPLICATIONS: LazyLock>> = result }); +#[expect(dead_code, reason = "stubs for rustc_target")] +enum Stability { + Stable, + Unstable(()), + InternalOnly { reason: &'static str, hard_error: bool }, +} +use Stability::*; + +type ImpliedFeatures = &'static [&'static str]; + // spellchecker:off -const TARGET_FEATURE_IMPLICATIONS_RAW: &[(&str, &[&str])] = &[ - // Arm - ("aes", &["neon"]), - ("dotprod", &["neon"]), - ("fp-armv8", &["vfp4"]), - ("fp16", &["neon"]), - ("i8mm", &["neon"]), - ("neon", &["vfp3"]), - ("sha2", &["neon"]), - ("v6", &["v5te"]), - ("v6k", &["v6"]), - ("v6t2", &["v6k", "thumb2"]), - ("v7", &["v6t2"]), - ("v8", &["v7"]), - ("vfp3", &["vfp2", "d32"]), - ("vfp4", &["vfp3"]), - // Aarch64 - ("aes", &["neon"]), - ("dotprod", &["neon"]), - ("dpb2", &["dpb"]), - ("f32mm", &["sve"]), - ("f64mm", &["sve"]), - ("fcma", &["neon"]), - ("fhm", &["fp16"]), - ("fp16", &["neon"]), - ("fp8", &["faminmax", "lut", "bf16"]), - ("fp8dot2", &["fp8dot4"]), - ("fp8dot4", &["fp8fma"]), - ("fp8fma", &["fp8"]), - ("jsconv", &["neon"]), - ("lse128", &["lse"]), - ("rcpc2", &["rcpc"]), - ("rcpc3", &["rcpc2"]), - ("rdm", &["neon"]), - ("sha2", &["neon"]), - ("sha3", &["sha2"]), - ("sm4", &["neon"]), - ("sme", &["bf16"]), - ("sme-b16b16", &["bf16", "sme2", "sve-b16b16"]), - ("sme-f16f16", &["sme2"]), - ("sme-f64f64", &["sme"]), - ("sme-f8f16", &["sme-f8f32"]), - ("sme-f8f32", &["sme2", "fp8"]), - ("sme-fa64", &["sme", "sve2"]), - ("sme-i16i64", &["sme"]), - ("sme2", &["sme"]), - ("sme2p1", &["sme2"]), - ("ssve-fp8dot2", &["ssve-fp8dot4"]), - ("ssve-fp8dot4", &["ssve-fp8fma"]), - ("ssve-fp8fma", &["sme2", "fp8"]), - ("sve", &["neon"]), - ("sve-b16b16", &["bf16"]), - ("sve2", &["sve"]), - ("sve2-aes", &["sve2", "aes"]), - ("sve2-bitperm", &["sve2"]), - ("sve2-sha3", &["sve2", "sha3"]), - ("sve2-sm4", &["sve2", "sm4"]), - ("sve2p1", &["sve2"]), - ("v8.1a", &["crc", "lse", "rdm", "pan", "lor", "vh"]), - ("v8.2a", &["v8.1a", "ras", "dpb"]), - ("v8.3a", &["v8.2a", "rcpc", "paca", "pacg", "jsconv"]), - ("v8.4a", &["v8.3a", "dotprod", "dit", "flagm"]), - ("v8.5a", &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), - ("v8.6a", &["v8.5a", "bf16", "i8mm"]), - ("v8.7a", &["v8.6a", "wfxt"]), - ("v8.8a", &["v8.7a", "hbc", "mops"]), - ("v8.9a", &["v8.8a", "cssc"]), - ("v9.1a", &["v9a", "v8.6a"]), - ("v9.2a", &["v9.1a", "v8.7a"]), - ("v9.3a", &["v9.2a", "v8.8a"]), - ("v9.4a", &["v9.3a", "v8.9a"]), - ("v9.5a", &["v9.4a"]), - ("v9a", &["v8.5a", "sve2"]), + +#[expect(non_upper_case_globals, reason = "stubs for rustc_target")] +mod sym { + // ARM + pub(super) const arm_target_feature: () = (); + // AArch64 + pub(super) const aarch64_unstable_target_feature: () = (); + pub(super) const aarch64_ver_target_feature: () = (); // x86 - ("aes", &["sse2"]), - ("amx-bf16", &["amx-tile"]), - ("amx-complex", &["amx-tile"]), - ("amx-fp16", &["amx-tile"]), - ("amx-int8", &["amx-tile"]), - ("avx", &["sse4.2"]), - ("avx2", &["avx"]), - ("avx512bf16", &["avx512bw"]), - ("avx512bitalg", &["avx512bw"]), - ("avx512bw", &["avx512f"]), - ("avx512cd", &["avx512f"]), - ("avx512dq", &["avx512f"]), - ("avx512f", &["avx2", "fma", "f16c"]), - ("avx512fp16", &["avx512bw", "avx512vl", "avx512dq"]), - ("avx512ifma", &["avx512f"]), - ("avx512vbmi", &["avx512bw"]), - ("avx512vbmi2", &["avx512bw"]), - ("avx512vl", &["avx512f"]), - ("avx512vnni", &["avx512f"]), - ("avx512vp2intersect", &["avx512f"]), - ("avx512vpopcntdq", &["avx512f"]), - ("avxifma", &["avx2"]), - ("avxneconvert", &["avx2"]), - ("avxvnni", &["avx2"]), - ("avxvnniint16", &["avx2"]), - ("avxvnniint8", &["avx2"]), - ("f16c", &["avx"]), - ("fma", &["avx"]), - ("gfni", &["sse2"]), - ("kl", &["sse2"]), - ("pclmulqdq", &["sse2"]), - ("sha", &["sse2"]), - ("sha512", &["avx2"]), - ("sm3", &["avx"]), - ("sm4", &["avx2"]), - ("sse2", &["sse"]), - ("sse3", &["sse2"]), - ("sse4.1", &["ssse3"]), - ("sse4.2", &["sse4.1"]), - ("sse4a", &["sse3"]), - ("ssse3", &["sse3"]), - ("vaes", &["avx2", "aes"]), - ("vpclmulqdq", &["avx", "pclmulqdq"]), - ("widekl", &["kl"]), - ("xop", &[/*"fma4", */ "avx", "sse4a"]), - ("xsavec", &["xsave"]), - ("xsaveopt", &["xsave"]), - ("xsaves", &["xsave"]), + pub(super) const x86_amx_intrinsics: () = (); + pub(super) const apx_target_feature: () = (); + pub(super) const avx10_target_feature: () = (); + pub(super) const clflushopt_target_feature: () = (); + pub(super) const fma4_target_feature: () = (); + pub(super) const ermsb_target_feature: () = (); + pub(super) const lahfsahf_target_feature: () = (); + pub(super) const movrs_target_feature: () = (); + pub(super) const prfchw_target_feature: () = (); + pub(super) const rtm_target_feature: () = (); + pub(super) const x87_target_feature: () = (); + pub(super) const xop_target_feature: () = (); + pub(super) const movdir64b_target_feature: () = (); + pub(super) const movdiri_target_feature: () = (); // Hexagon - ("hvx-length128b", &["hvx"]), + pub(super) const hexagon_target_feature: () = (); // PowerPC - ("power10-vector", &["power9-vector"]), - ("power8-altivec", &["altivec"]), - ("power8-crypto", &["power8-altivec"]), - ("power8-vector", &["vsx", "power8-altivec"]), - ("power9-altivec", &["power8-altivec"]), - ("power9-vector", &["power8-vector", "power9-altivec"]), - ("vsx", &["altivec"]), + pub(super) const powerpc_target_feature: () = (); // MIPS + pub(super) const mips_target_feature: () = (); + // NVPTX + pub(super) const nvptx_target_feature: () = (); // RISC-V - ("a", &["zaamo", "zalrsc"]), - ("d", &["f"]), - ("m", &["zmmul"]), - ("zabha", &["zaamo"]), - ("zdinx", &["zfinx"]), - ("zfh", &["zfhmin"]), - ("zfhmin", &["f"]), - ("zhinx", &["zhinxmin"]), - ("zhinxmin", &["zfinx"]), - ("zk", &["zkn", "zkr", "zkt"]), - ("zkn", &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), - ("zks", &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), + pub(super) const riscv_target_feature: () = (); // WASM - ("relaxed-simd", &["simd128"]), + pub(super) const wasm_target_feature: () = (); // BPF - ("alu32", &[]), - ("allows-misaligned-mem-access", &[]), + pub(super) const bpf_target_feature: () = (); // CSKY - ("10e60", &["7e10"]), - ("2e3", &["e2"]), - ("3e3r2", &["3e3r1", "doloop"]), - ("3e3r3", &["doloop"]), - ("3e7", &["2e3"]), - ("7e10", &["3e7"]), - ("e1", &["elrw"]), - ("e2", &["e2"]), - ("mp", &["2e3"]), - ("mp1e2", &["3e7"]), + pub(super) const csky_target_feature: () = (); // LoongArch - ("d", &["f"]), - ("lasx", &["lsx"]), - ("lsx", &["d"]), - // IBM Z - ("nnp-assist", &["vector"]), - ("vector-enhancements-1", &["vector"]), - ("vector-enhancements-2", &["vector-enhancements-1"]), - ("vector-packed-decimal", &["vector"]), - ("vector-packed-decimal-enhancement", &["vector-packed-decimal"]), - ("vector-packed-decimal-enhancement-2", &["vector-packed-decimal-enhancement"]), + pub(super) const loongarch_target_feature: () = (); + // s390x + pub(super) const s390x_target_feature: () = (); // SPARC + pub(super) const sparc_target_feature: () = (); // m68k - ("isa-68010", &["isa-68000"]), - ("isa-68020", &["isa-68010"]), - ("isa-68030", &["isa-68020"]), - ("isa-68040", &["isa-68030", "isa-68882"]), - ("isa-68060", &["isa-68040"]), - ("isa-68882", &["isa-68881"]), + pub(super) const m68k_target_feature: () = (); + // AVR + pub(super) const avr_target_feature: () = (); + // Xtensa + pub(super) const xtensa_target_feature: () = (); +} + +// The following code is copied verbatim from compiler/rustc_target/src/target_features.rs. + +static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("aclass", Unstable(sym::arm_target_feature), &[]), + ("acquire-release", Unstable(sym::arm_target_feature), &[]), + ("aes", Unstable(sym::arm_target_feature), &["neon"]), + ( + "atomics-32", + // Not implied by any CPU model or other feature. + Stability::InternalOnly { + reason: "unsound because it changes the ABI of atomic operations", + hard_error: false, + }, + &[], + ), + ("crc", Unstable(sym::arm_target_feature), &[]), + ("d32", Unstable(sym::arm_target_feature), &[]), + ("dotprod", Unstable(sym::arm_target_feature), &["neon"]), + ("dsp", Unstable(sym::arm_target_feature), &[]), + ("fp-armv8", Unstable(sym::arm_target_feature), &["vfp4"]), + ("fp16", Unstable(sym::arm_target_feature), &["neon"]), + ("fp64", Unstable(sym::arm_target_feature), &[]), + ("fpregs", Unstable(sym::arm_target_feature), &[]), + ("i8mm", Unstable(sym::arm_target_feature), &["neon"]), + ("mclass", Unstable(sym::arm_target_feature), &[]), + ("mve", Unstable(sym::arm_target_feature), &["v8.1m.main", "dsp", "fpregs"]), + ("mve.fp", Unstable(sym::arm_target_feature), &["mve"]), + ("neon", Unstable(sym::arm_target_feature), &["vfp3"]), + ("rclass", Unstable(sym::arm_target_feature), &[]), + ("sha2", Unstable(sym::arm_target_feature), &["neon"]), + // This can be *disabled* on non-`hf` targets to enable the use + // of hardfloats while keeping the softfloat ABI. + // FIXME before stabilization: Should we expose this as a `hard-float` target feature instead of + // matching the odd negative feature LLVM uses? + ("soft-float", Unstable(sym::arm_target_feature), &[]), + // This is needed for inline assembly, but shouldn't be stabilized as-is + // since it should be enabled per-function using #[instruction_set], not + // #[target_feature]. + ("thumb-mode", Unstable(sym::arm_target_feature), &[]), + ("thumb2", Unstable(sym::arm_target_feature), &[]), + ("trustzone", Unstable(sym::arm_target_feature), &[]), + ("v5te", Unstable(sym::arm_target_feature), &[]), + ("v6", Unstable(sym::arm_target_feature), &["v5te"]), + ("v6k", Unstable(sym::arm_target_feature), &["v6"]), + ("v6m", Unstable(sym::arm_target_feature), &["v6"]), + ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "v8m", "thumb2"]), + ("v7", Unstable(sym::arm_target_feature), &["v6t2"]), + ("v8", Unstable(sym::arm_target_feature), &["v7", "acquire-release"]), + ("v8.1m.main", Unstable(sym::arm_target_feature), &["v8m.main"]), + ("v8m", Unstable(sym::arm_target_feature), &["v6m"]), + ("v8m.main", Unstable(sym::arm_target_feature), &["v7"]), + ("vfp2", Unstable(sym::arm_target_feature), &["vfp2sp", "fp64"]), + ("vfp2sp", Unstable(sym::arm_target_feature), &["fpregs"]), + ("vfp3", Unstable(sym::arm_target_feature), &["vfp2", "d32"]), + ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), + ("virtualization", Unstable(sym::arm_target_feature), &[]), + // tidy-alphabetical-end +]; + +static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + // FEAT_AES & FEAT_PMULL + ("aes", Stable, &["neon"]), + // FEAT_BF16 + ("bf16", Stable, &[]), + // FEAT_BTI + ("bti", Stable, &[]), + // FEAT_CRC + ("crc", Stable, &[]), + // FEAT_CSSC + ("cssc", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_DIT + ("dit", Stable, &[]), + // FEAT_DotProd + ("dotprod", Stable, &["neon"]), + // FEAT_DPB + ("dpb", Stable, &[]), + // FEAT_DPB2 + ("dpb2", Stable, &["dpb"]), + // FEAT_ECV + ("ecv", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_F32MM + ("f32mm", Stable, &["sve"]), + // FEAT_F64MM + ("f64mm", Stable, &["sve"]), + // FEAT_FAMINMAX + ("faminmax", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_FCMA + ("fcma", Stable, &["neon"]), + // FEAT_FHM + ("fhm", Stable, &["fp16"]), + // FEAT_FLAGM + ("flagm", Stable, &[]), + // FEAT_FLAGM2 + ("flagm2", Unstable(sym::aarch64_unstable_target_feature), &[]), + // We forbid directly toggling just `fp-armv8`; it must be toggled with `neon`. + ( + "fp-armv8", + // Pinned down by [`Target::abi_required_features`] when needed. + Stability::InternalOnly { reason: "Rust ties `fp-armv8` to `neon`", hard_error: false }, + &[], + ), + // FEAT_FP8 + ("fp8", Unstable(sym::aarch64_unstable_target_feature), &["faminmax", "lut", "bf16"]), + // FEAT_FP8DOT2 + ("fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["fp8dot4"]), + // FEAT_FP8DOT4 + ("fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["fp8fma"]), + // FEAT_FP8FMA + ("fp8fma", Unstable(sym::aarch64_unstable_target_feature), &["fp8"]), + // FEAT_FP16 + // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 + ("fp16", Stable, &["neon"]), + // FEAT_FRINTTS + ("frintts", Stable, &[]), + // FEAT_HBC + ("hbc", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_I8MM + ("i8mm", Stable, &[]), + // FEAT_JSCVT + // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 + ("jsconv", Stable, &["neon"]), + // FEAT_LOR + ("lor", Stable, &[]), + // FEAT_LSE + ("lse", Stable, &[]), + // FEAT_LSE2 + ("lse2", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_LSE128 + ("lse128", Unstable(sym::aarch64_unstable_target_feature), &["lse"]), + // FEAT_LUT + ("lut", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_MOPS + ("mops", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_MTE & FEAT_MTE2 + ("mte", Stable, &[]), + // FEAT_AdvSimd & FEAT_FP + ("neon", Stable, &[]), + // Backend option to turn atomic operations into an intrinsic call when `lse` is not known to be + // available, so the intrinsic can do runtime LSE feature detection rather than unconditionally + // using slower non-LSE operations. Unstable since it doesn't need to user-togglable. + ("outline-atomics", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_PAUTH (address authentication) + ("paca", Stable, &[]), + // FEAT_PAUTH (generic authentication) + ("pacg", Stable, &[]), + // FEAT_PAN + ("pan", Stable, &[]), + // FEAT_PAuth_LR + ("pauth-lr", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_PMUv3 + ("pmuv3", Stable, &[]), + // FEAT_RNG + ("rand", Stable, &[]), + // FEAT_RAS & FEAT_RASv1p1 + ("ras", Stable, &[]), + // FEAT_LRCPC + ("rcpc", Stable, &[]), + // FEAT_LRCPC2 + ("rcpc2", Stable, &["rcpc"]), + // FEAT_LRCPC3 + ("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), + // FEAT_RDM + ("rdm", Stable, &["neon"]), + ( + "reserve-x18", + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + InternalOnly { reason: "use `-Zfixed-x18` compiler flag instead", hard_error: false }, + &[], + ), + // FEAT_SB + ("sb", Stable, &[]), + // FEAT_SHA1 & FEAT_SHA256 + ("sha2", Stable, &["neon"]), + // FEAT_SHA512 & FEAT_SHA3 + ("sha3", Stable, &["sha2"]), + // FEAT_SM3 & FEAT_SM4 + ("sm4", Stable, &["neon"]), + // FEAT_SME + ("sme", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]), + // FEAT_SME_B16B16 + ("sme-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16", "sme2", "sve-b16b16"]), + // FEAT_SME_F8F16 + ("sme-f8f16", Unstable(sym::aarch64_unstable_target_feature), &["sme-f8f32"]), + // FEAT_SME_F8F32 + ("sme-f8f32", Unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), + // FEAT_SME_F16F16 + ("sme-f16f16", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]), + // FEAT_SME_F64F64 + ("sme-f64f64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), + // FEAT_SME_FA64 + ("sme-fa64", Unstable(sym::aarch64_unstable_target_feature), &["sme", "sve2"]), + // FEAT_SME_I16I64 + ("sme-i16i64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), + // FEAT_SME_LUTv2 + ("sme-lutv2", Unstable(sym::aarch64_unstable_target_feature), &[]), + // FEAT_SME2 + ("sme2", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), + // FEAT_SME2p1 + ("sme2p1", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]), + // FEAT_SPE + ("spe", Stable, &[]), + // FEAT_SSBS & FEAT_SSBS2 + ("ssbs", Stable, &[]), + // FEAT_SSVE_FP8FDOT2 + ("ssve-fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8dot4"]), + // FEAT_SSVE_FP8FDOT4 + ("ssve-fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8fma"]), + // FEAT_SSVE_FP8FMA + ("ssve-fp8fma", Unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), + // FEAT_SVE + // It was decided that SVE requires Neon: https://github.com/rust-lang/rust/pull/91608 + // + // LLVM doesn't enable Neon for SVE. ARM indicates that they're separate, but probably always + // exist together: https://developer.arm.com/documentation/102340/0100/New-features-in-SVE2 + // + // "For backwards compatibility, Neon and VFP are required in the latest architectures." + ("sve", Stable, &["neon", "fp16"]), + // FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions) + ("sve-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]), + // FEAT_SVE2 + ("sve2", Stable, &["sve"]), + // FEAT_SVE_AES & FEAT_SVE_PMULL128 + ("sve2-aes", Stable, &["sve2", "aes"]), + // FEAT_SVE2_BitPerm + ("sve2-bitperm", Stable, &["sve2"]), + // FEAT_SVE2_SHA3 + ("sve2-sha3", Stable, &["sve2", "sha3"]), + // FEAT_SVE2_SM4 + ("sve2-sm4", Stable, &["sve2", "sm4"]), + // FEAT_SVE2p1 + ("sve2p1", Unstable(sym::aarch64_unstable_target_feature), &["sve2"]), + // FEAT_TME + ("tme", Stable, &[]), + ( + "v8.1a", + Unstable(sym::aarch64_ver_target_feature), + &["crc", "lse", "rdm", "pan", "lor", "vh"], + ), + ("v8.2a", Unstable(sym::aarch64_ver_target_feature), &["v8.1a", "ras", "dpb"]), + ( + "v8.3a", + Unstable(sym::aarch64_ver_target_feature), + &["v8.2a", "rcpc", "paca", "pacg", "jsconv"], + ), + ("v8.4a", Unstable(sym::aarch64_ver_target_feature), &["v8.3a", "dotprod", "dit", "flagm"]), + ("v8.5a", Unstable(sym::aarch64_ver_target_feature), &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), + ("v8.6a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "bf16", "i8mm"]), + ("v8.7a", Unstable(sym::aarch64_ver_target_feature), &["v8.6a", "wfxt"]), + ("v8.8a", Unstable(sym::aarch64_ver_target_feature), &["v8.7a", "hbc", "mops"]), + ("v8.9a", Unstable(sym::aarch64_ver_target_feature), &["v8.8a", "cssc"]), + ("v9.1a", Unstable(sym::aarch64_ver_target_feature), &["v9a", "v8.6a"]), + ("v9.2a", Unstable(sym::aarch64_ver_target_feature), &["v9.1a", "v8.7a"]), + ("v9.3a", Unstable(sym::aarch64_ver_target_feature), &["v9.2a", "v8.8a"]), + ("v9.4a", Unstable(sym::aarch64_ver_target_feature), &["v9.3a", "v8.9a"]), + ("v9.5a", Unstable(sym::aarch64_ver_target_feature), &["v9.4a"]), + ("v9a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a"]), + // FEAT_VHE + ("vh", Stable, &[]), + // FEAT_WFxT + ("wfxt", Unstable(sym::aarch64_unstable_target_feature), &[]), + // tidy-alphabetical-end +]; + +static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("adx", Stable, &[]), + ("aes", Stable, &["sse2"]), + ("amx-avx512", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-complex", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-fp8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-movrs", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), + ("apxf", Unstable(sym::apx_target_feature), &[]), + ("avx", Stable, &["sse4.2"]), + ("avx2", Stable, &["avx"]), + ( + "avx10.1", + Unstable(sym::avx10_target_feature), + &[ + "avx512bf16", + "avx512bitalg", + "avx512bw", + "avx512cd", + "avx512dq", + "avx512f", + "avx512fp16", + "avx512ifma", + "avx512vbmi", + "avx512vbmi2", + "avx512vl", + "avx512vnni", + "avx512vpopcntdq", + ], + ), + ( + "avx10.2", + Unstable(sym::avx10_target_feature), + &["avx10.1", "avxvnni", "avxvnniint8", "avxvnniint16"], + ), + ("avx512bf16", Stable, &["avx512bw"]), + ("avx512bitalg", Stable, &["avx512bw"]), + ("avx512bw", Stable, &["avx512f"]), + ("avx512cd", Stable, &["avx512f"]), + ("avx512dq", Stable, &["avx512f"]), + ("avx512f", Stable, &["avx2", "fma", "f16c"]), + ("avx512fp16", Stable, &["avx512bw"]), + ("avx512ifma", Stable, &["avx512f"]), + ("avx512vbmi", Stable, &["avx512bw"]), + ("avx512vbmi2", Stable, &["avx512bw"]), + ("avx512vl", Stable, &["avx512f"]), + ("avx512vnni", Stable, &["avx512f"]), + ("avx512vp2intersect", Stable, &["avx512f"]), + ("avx512vpopcntdq", Stable, &["avx512f"]), + ("avxifma", Stable, &["avx2"]), + ("avxneconvert", Stable, &["avx2"]), + ("avxvnni", Stable, &["avx2"]), + ("avxvnniint8", Stable, &["avx2"]), + ("avxvnniint16", Stable, &["avx2"]), + ("bmi1", Stable, &[]), + ("bmi2", Stable, &[]), + ("clflushopt", Unstable(sym::clflushopt_target_feature), &[]), + ("cmpxchg16b", Stable, &[]), + ("ermsb", Unstable(sym::ermsb_target_feature), &[]), + ("f16c", Stable, &["avx"]), + ("fma", Stable, &["avx"]), + ("fma4", Unstable(sym::fma4_target_feature), &["avx", "sse4a"]), + ("fxsr", Stable, &[]), + ("gfni", Stable, &["sse2"]), + ("kl", Stable, &["sse2"]), + ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), + ("lzcnt", Stable, &[]), + ("movbe", Stable, &[]), + ("movdir64b", Unstable(sym::movdir64b_target_feature), &[]), + ("movdiri", Unstable(sym::movdiri_target_feature), &[]), + ("movrs", Unstable(sym::movrs_target_feature), &[]), + ("pclmulqdq", Stable, &["sse2"]), + ("popcnt", Stable, &[]), + ("prfchw", Unstable(sym::prfchw_target_feature), &[]), + ("rdrand", Stable, &[]), + ("rdseed", Stable, &[]), + ( + "retpoline-external-thunk", + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + Stability::InternalOnly { + reason: "use `-Zretpoline-external-thunk` compiler flag instead", + hard_error: false, + }, + &[], + ), + ( + "retpoline-indirect-branches", + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + Stability::InternalOnly { + reason: "use `-Zretpoline` compiler flag instead", + hard_error: false, + }, + &[], + ), + ( + "retpoline-indirect-calls", + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + Stability::InternalOnly { + reason: "use `-Zretpoline` compiler flag instead", + hard_error: false, + }, + &[], + ), + ("rtm", Unstable(sym::rtm_target_feature), &[]), + ("sha", Stable, &["sse2"]), + ("sha512", Stable, &["avx2"]), + ("sm3", Stable, &["avx"]), + ("sm4", Stable, &["avx2"]), + ( + "soft-float", + // Pinned down by [`Target::abi_required_features`]. + Stability::InternalOnly { reason: "use a soft-float target instead", hard_error: false }, + &[], + ), + ("sse", Stable, &[]), + ("sse2", Stable, &["sse"]), + ("sse3", Stable, &["sse2"]), + ("sse4.1", Stable, &["ssse3"]), + ("sse4.2", Stable, &["sse4.1"]), + ("sse4a", Stable, &["sse3"]), + ("ssse3", Stable, &["sse3"]), + ("tbm", Stable, &[]), + ("vaes", Stable, &["avx2", "aes"]), + ("vpclmulqdq", Stable, &["avx", "pclmulqdq"]), + ("widekl", Stable, &["kl"]), + ("x87", Unstable(sym::x87_target_feature), &[]), + ("xop", Unstable(sym::xop_target_feature), &["fma4", "avx", "sse4a"]), + ("xsave", Stable, &[]), + ("xsavec", Stable, &["xsave"]), + ("xsaveopt", Stable, &["xsave"]), + ("xsaves", Stable, &["xsave"]), + // tidy-alphabetical-end +]; + +const HEXAGON_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("audio", Unstable(sym::hexagon_target_feature), &[]), + ("hvx", Unstable(sym::hexagon_target_feature), &[]), + ("hvx-ieee-fp", Unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvx-length64b", Unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvx-qfloat", Unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvxv60", Unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvxv62", Unstable(sym::hexagon_target_feature), &["hvxv60"]), + ("hvxv65", Unstable(sym::hexagon_target_feature), &["hvxv62"]), + ("hvxv66", Unstable(sym::hexagon_target_feature), &["hvxv65", "zreg"]), + ("hvxv67", Unstable(sym::hexagon_target_feature), &["hvxv66"]), + ("hvxv68", Unstable(sym::hexagon_target_feature), &["hvxv67"]), + ("hvxv69", Unstable(sym::hexagon_target_feature), &["hvxv68"]), + ("hvxv71", Unstable(sym::hexagon_target_feature), &["hvxv69"]), + ("hvxv73", Unstable(sym::hexagon_target_feature), &["hvxv71"]), + ("hvxv75", Unstable(sym::hexagon_target_feature), &["hvxv73"]), + ("hvxv79", Unstable(sym::hexagon_target_feature), &["hvxv75"]), + ("v60", Unstable(sym::hexagon_target_feature), &[]), + ("v62", Unstable(sym::hexagon_target_feature), &["v60"]), + ("v65", Unstable(sym::hexagon_target_feature), &["v62"]), + ("v66", Unstable(sym::hexagon_target_feature), &["v65"]), + ("v67", Unstable(sym::hexagon_target_feature), &["v66"]), + ("v68", Unstable(sym::hexagon_target_feature), &["v67"]), + ("v69", Unstable(sym::hexagon_target_feature), &["v68"]), + ("v71", Unstable(sym::hexagon_target_feature), &["v69"]), + ("v73", Unstable(sym::hexagon_target_feature), &["v71"]), + ("v75", Unstable(sym::hexagon_target_feature), &["v73"]), + ("v79", Unstable(sym::hexagon_target_feature), &["v75"]), + ("zreg", Unstable(sym::hexagon_target_feature), &[]), + // tidy-alphabetical-end +]; + +static POWERPC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // If you are thinking of adding "efpu2" here, please double-check that it really does not + // affect the ABI. + // tidy-alphabetical-start + ("altivec", Unstable(sym::powerpc_target_feature), &[]), + ( + "hard-float", + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, + &[], + ), + ("msync", Unstable(sym::powerpc_target_feature), &[]), + ("partword-atomics", Unstable(sym::powerpc_target_feature), &[]), + ("power8-altivec", Unstable(sym::powerpc_target_feature), &["altivec"]), + ("power8-crypto", Unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power8-vector", Unstable(sym::powerpc_target_feature), &["vsx", "power8-altivec"]), + ("power9-altivec", Unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power9-vector", Unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), + ("power10-vector", Unstable(sym::powerpc_target_feature), &["power9-vector"]), + ("quadword-atomics", Unstable(sym::powerpc_target_feature), &[]), + ( + "spe", + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, + &[], + ), + ("vsx", Unstable(sym::powerpc_target_feature), &["altivec"]), + // tidy-alphabetical-end +]; + +const MIPS_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("fp64", Unstable(sym::mips_target_feature), &[]), + // FIXME(#150253): msa requires either fp64 or no hard float support at all: LLVM requires fp64 + // FIXME(#150253): msa requires revision 5 or greater (mips32r5/mips64r5 in LLVM) + ("msa", Unstable(sym::mips_target_feature), &[]), + ("virt", Unstable(sym::mips_target_feature), &[]), + // tidy-alphabetical-end +]; + +const NVPTX_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("sm_70", Unstable(sym::nvptx_target_feature), &[]), + ("sm_72", Unstable(sym::nvptx_target_feature), &["sm_70"]), + ("sm_75", Unstable(sym::nvptx_target_feature), &["sm_72"]), + ("sm_80", Unstable(sym::nvptx_target_feature), &["sm_75"]), + ("sm_86", Unstable(sym::nvptx_target_feature), &["sm_80"]), + ("sm_87", Unstable(sym::nvptx_target_feature), &["sm_86"]), + ("sm_89", Unstable(sym::nvptx_target_feature), &["sm_87"]), + ("sm_90", Unstable(sym::nvptx_target_feature), &["sm_89"]), + ("sm_90a", Unstable(sym::nvptx_target_feature), &["sm_90"]), + // tidy-alphabetical-end + // tidy-alphabetical-start + ("sm_100", Unstable(sym::nvptx_target_feature), &["sm_90"]), + ("sm_100a", Unstable(sym::nvptx_target_feature), &["sm_100"]), + ("sm_101", Unstable(sym::nvptx_target_feature), &["sm_100"]), + ("sm_101a", Unstable(sym::nvptx_target_feature), &["sm_101"]), + ("sm_120", Unstable(sym::nvptx_target_feature), &["sm_101"]), + ("sm_120a", Unstable(sym::nvptx_target_feature), &["sm_120"]), + // tidy-alphabetical-end + // tidy-alphabetical-start + ("ptx70", Unstable(sym::nvptx_target_feature), &[]), + ("ptx71", Unstable(sym::nvptx_target_feature), &["ptx70"]), + ("ptx72", Unstable(sym::nvptx_target_feature), &["ptx71"]), + ("ptx73", Unstable(sym::nvptx_target_feature), &["ptx72"]), + ("ptx74", Unstable(sym::nvptx_target_feature), &["ptx73"]), + ("ptx75", Unstable(sym::nvptx_target_feature), &["ptx74"]), + ("ptx76", Unstable(sym::nvptx_target_feature), &["ptx75"]), + ("ptx77", Unstable(sym::nvptx_target_feature), &["ptx76"]), + ("ptx78", Unstable(sym::nvptx_target_feature), &["ptx77"]), + ("ptx80", Unstable(sym::nvptx_target_feature), &["ptx78"]), + ("ptx81", Unstable(sym::nvptx_target_feature), &["ptx80"]), + ("ptx82", Unstable(sym::nvptx_target_feature), &["ptx81"]), + ("ptx83", Unstable(sym::nvptx_target_feature), &["ptx82"]), + ("ptx84", Unstable(sym::nvptx_target_feature), &["ptx83"]), + ("ptx85", Unstable(sym::nvptx_target_feature), &["ptx84"]), + ("ptx86", Unstable(sym::nvptx_target_feature), &["ptx85"]), + ("ptx87", Unstable(sym::nvptx_target_feature), &["ptx86"]), + // tidy-alphabetical-end +]; + +static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("a", Stable, &["zaamo", "zalrsc"]), + ("b", Stable, &["zba", "zbb", "zbs"]), + ("c", Stable, &["zca"]), + ("d", Stable, &["f"]), + ("e", Unstable(sym::riscv_target_feature), &[]), // negative feature! needs special care. + ("f", Stable, &["zicsr"]), + ( + "forced-atomics", + // Not implied by any CPU model or other feature. + Stability::InternalOnly { + reason: "unsound because it changes the ABI of atomic operations", + hard_error: false, + }, + &[], + ), + // According to the RISC-V spec, the M ISA extension (integer multiplication/division) is supported only when the M bit + // of the misa register is 1, while Zmmul means integer multiplication is always supported. + // + // The Rust/LLVM "m" target feature means something slightly different: with "m" enabled, it is assumed that integer + // multiplication and division will always be available (M bit will be 1 in misa), so "m" implies "zmmul". + // + // See discussion in the PR adding Zmmul: https://github.com/rust-lang/rust/pull/162552 + ("m", Stable, &["zmmul"]), + ("relax", Unstable(sym::riscv_target_feature), &[]), + ( + "rva23u64", + Unstable(sym::riscv_target_feature), + &[ + "m", + "a", + "f", + "d", + "c", + "b", + "v", + "zicsr", + "zicntr", + "zihpm", + "ziccif", + "ziccrse", + "ziccamoa", + "zicclsm", + "zic64b", + "za64rs", + "zihintpause", + "zba", + "zbb", + "zbs", + "zicbom", + "zicbop", + "zicboz", + "zfhmin", + "zkt", + "zvfhmin", + "zvbb", + "zvkt", + "zihintntl", + "zicond", + "zimop", + "zcmop", + "zcb", + "zfa", + "zawrs", + "supm", + ], + ), + ("supm", Unstable(sym::riscv_target_feature), &[]), + ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature), &[]), + ("unaligned-vector-mem", Unstable(sym::riscv_target_feature), &[]), + ("v", Unstable(sym::riscv_target_feature), &["zvl128b", "zve64d"]), + ("za64rs", Stable, &["za128rs"]), // Za64rs βŠƒ Za128rs + ("za128rs", Stable, &[]), + ("zaamo", Stable, &[]), + ("zabha", Stable, &["zaamo"]), + ("zacas", Stable, &["zaamo"]), + ("zalrsc", Stable, &[]), + ("zama16b", Stable, &[]), + ("zawrs", Stable, &[]), + ("zba", Stable, &[]), + ("zbb", Stable, &[]), + ("zbc", Stable, &["zbkc"]), // Zbc βŠƒ Zbkc + ("zbkb", Stable, &[]), + ("zbkc", Stable, &[]), + ("zbkx", Stable, &[]), + ("zbs", Stable, &[]), + ("zca", Stable, &[]), + ("zcb", Stable, &["zca"]), + ("zcmop", Stable, &["zca"]), + ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zfa", Unstable(sym::riscv_target_feature), &["f"]), + ("zfbfmin", Unstable(sym::riscv_target_feature), &["f"]), // and a subset of Zfhmin + ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]), + ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]), + ("zfinx", Unstable(sym::riscv_target_feature), &["zicsr"]), + ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]), + ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zic64b", Stable, &[]), + ("zicbom", Stable, &[]), + ("zicbop", Stable, &[]), + ("zicboz", Stable, &[]), + ("ziccamoa", Stable, &[]), + ("ziccif", Stable, &[]), + ("zicclsm", Stable, &[]), + ("ziccrse", Stable, &[]), + ("zicntr", Stable, &["zicsr"]), + ("zicond", Stable, &[]), + ("zicsr", Stable, &[]), + ("zifencei", Stable, &[]), + ("zihintntl", Stable, &[]), + ("zihintpause", Stable, &[]), + ("zihpm", Stable, &["zicsr"]), + ("zimop", Stable, &[]), + ("zk", Stable, &["zkn", "zkr", "zkt"]), + ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), + ("zknd", Stable, &["zkne_or_zknd"]), + ("zkne", Stable, &["zkne_or_zknd"]), + ("zkne_or_zknd", Unstable(sym::riscv_target_feature), &[]), // Not an extension + ("zknh", Stable, &[]), + ("zkr", Stable, &[]), + ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), + ("zksed", Stable, &[]), + ("zksh", Stable, &[]), + ("zkt", Stable, &[]), + ("zmmul", Unstable(sym::riscv_target_feature), &[]), + ("ztso", Stable, &[]), + ("zvbb", Unstable(sym::riscv_target_feature), &["zvkb"]), // Zvbb βŠƒ Zvkb + ("zvbc", Unstable(sym::riscv_target_feature), &["zve64x"]), + ("zve32f", Unstable(sym::riscv_target_feature), &["zve32x", "f"]), + ("zve32x", Unstable(sym::riscv_target_feature), &["zvl32b", "zicsr"]), + ("zve64d", Unstable(sym::riscv_target_feature), &["zve64f", "d"]), + ("zve64f", Unstable(sym::riscv_target_feature), &["zve32f", "zve64x"]), + ("zve64x", Unstable(sym::riscv_target_feature), &["zve32x", "zvl64b"]), + ("zvfbfmin", Unstable(sym::riscv_target_feature), &["zve32f"]), + ("zvfbfwma", Unstable(sym::riscv_target_feature), &["zfbfmin", "zvfbfmin"]), + ("zvfh", Unstable(sym::riscv_target_feature), &["zvfhmin", "zve32f", "zfhmin"]), // Zvfh βŠƒ Zvfhmin + ("zvfhmin", Unstable(sym::riscv_target_feature), &["zve32f"]), + ("zvkb", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvkg", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvkn", Unstable(sym::riscv_target_feature), &["zvkned", "zvknhb", "zvkb", "zvkt"]), + ("zvknc", Unstable(sym::riscv_target_feature), &["zvkn", "zvbc"]), + ("zvkned", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvkng", Unstable(sym::riscv_target_feature), &["zvkn", "zvkg"]), + ("zvknha", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvknhb", Unstable(sym::riscv_target_feature), &["zvknha", "zve64x"]), // Zvknhb βŠƒ Zvknha + ("zvks", Unstable(sym::riscv_target_feature), &["zvksed", "zvksh", "zvkb", "zvkt"]), + ("zvksc", Unstable(sym::riscv_target_feature), &["zvks", "zvbc"]), + ("zvksed", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvksg", Unstable(sym::riscv_target_feature), &["zvks", "zvkg"]), + ("zvksh", Unstable(sym::riscv_target_feature), &["zve32x"]), + ("zvkt", Unstable(sym::riscv_target_feature), &[]), + ("zvl32b", Unstable(sym::riscv_target_feature), &[]), + ("zvl64b", Unstable(sym::riscv_target_feature), &["zvl32b"]), + ("zvl128b", Unstable(sym::riscv_target_feature), &["zvl64b"]), + ("zvl256b", Unstable(sym::riscv_target_feature), &["zvl128b"]), + ("zvl512b", Unstable(sym::riscv_target_feature), &["zvl256b"]), + ("zvl1024b", Unstable(sym::riscv_target_feature), &["zvl512b"]), + ("zvl2048b", Unstable(sym::riscv_target_feature), &["zvl1024b"]), + ("zvl4096b", Unstable(sym::riscv_target_feature), &["zvl2048b"]), + ("zvl8192b", Unstable(sym::riscv_target_feature), &["zvl4096b"]), + ("zvl16384b", Unstable(sym::riscv_target_feature), &["zvl8192b"]), + ("zvl32768b", Unstable(sym::riscv_target_feature), &["zvl16384b"]), + ("zvl65536b", Unstable(sym::riscv_target_feature), &["zvl32768b"]), + // tidy-alphabetical-end +]; + +static WASM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("atomics", Unstable(sym::wasm_target_feature), &[]), + ("bulk-memory", Stable, &[]), + ("exception-handling", Unstable(sym::wasm_target_feature), &[]), + ("extended-const", Stable, &[]), + ("gc", Unstable(sym::wasm_target_feature), &["reference-types"]), + ("multivalue", Stable, &[]), + ("mutable-globals", Stable, &[]), + ("nontrapping-fptoint", Stable, &[]), + ("reference-types", Stable, &[]), + ("relaxed-simd", Stable, &["simd128"]), + ("sign-ext", Stable, &[]), + ("simd128", Stable, &[]), + ("tail-call", Stable, &[]), + ("wide-arithmetic", Unstable(sym::wasm_target_feature), &[]), + // tidy-alphabetical-end +]; + +const BPF_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + ("alu32", Unstable(sym::bpf_target_feature), &[]), + ("allows-misaligned-mem-access", Unstable(sym::bpf_target_feature), &[]), +]; + +static CSKY_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("2e3", Unstable(sym::csky_target_feature), &["e2"]), + ("3e3r1", Unstable(sym::csky_target_feature), &[]), + ("3e3r2", Unstable(sym::csky_target_feature), &["3e3r1", "doloop"]), + ("3e3r3", Unstable(sym::csky_target_feature), &["doloop"]), + ("3e7", Unstable(sym::csky_target_feature), &["2e3"]), + ("7e10", Unstable(sym::csky_target_feature), &["3e7"]), + ("10e60", Unstable(sym::csky_target_feature), &["7e10"]), + ("cache", Unstable(sym::csky_target_feature), &[]), + ("doloop", Unstable(sym::csky_target_feature), &[]), + ("dsp1e2", Unstable(sym::csky_target_feature), &[]), + ("dspe60", Unstable(sym::csky_target_feature), &[]), + ("e1", Unstable(sym::csky_target_feature), &["elrw"]), + ("e2", Unstable(sym::csky_target_feature), &["e2"]), + ("edsp", Unstable(sym::csky_target_feature), &[]), + ("elrw", Unstable(sym::csky_target_feature), &[]), + ("float1e2", Unstable(sym::csky_target_feature), &[]), + ("float1e3", Unstable(sym::csky_target_feature), &[]), + ("float3e4", Unstable(sym::csky_target_feature), &[]), + ("float7e60", Unstable(sym::csky_target_feature), &[]), + ("floate1", Unstable(sym::csky_target_feature), &[]), + ("hard-tp", Unstable(sym::csky_target_feature), &[]), + ("high-registers", Unstable(sym::csky_target_feature), &[]), + ("hwdiv", Unstable(sym::csky_target_feature), &[]), + ("mp", Unstable(sym::csky_target_feature), &["2e3"]), + ("mp1e2", Unstable(sym::csky_target_feature), &["3e7"]), + ("nvic", Unstable(sym::csky_target_feature), &[]), + ("trust", Unstable(sym::csky_target_feature), &[]), + ("vdsp2e60f", Unstable(sym::csky_target_feature), &[]), + ("vdspv1", Unstable(sym::csky_target_feature), &[]), + ("vdspv2", Unstable(sym::csky_target_feature), &[]), + // tidy-alphabetical-end + //fpu + // tidy-alphabetical-start + ("fdivdu", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_sf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hi", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_sf", Unstable(sym::csky_target_feature), &[]), + ("hard-float", Unstable(sym::csky_target_feature), &[]), + ("hard-float-abi", Unstable(sym::csky_target_feature), &[]), + // tidy-alphabetical-end +]; + +static LOONGARCH_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("32s", Unstable(sym::loongarch_target_feature), &[]), + ("d", Stable, &["f"]), + ("div32", Stable, &[]), + ("f", Stable, &[]), + ("frecipe", Stable, &[]), + ("lam-bh", Stable, &[]), + ("lamcas", Stable, &[]), + ("lasx", Stable, &["lsx"]), + ("lbt", Stable, &[]), + ("ld-seq-sa", Stable, &[]), + ("lsx", Stable, &["d"]), + ("lvz", Stable, &[]), + ("relax", Unstable(sym::loongarch_target_feature), &[]), + ("scq", Stable, &[]), + ("ual", Unstable(sym::loongarch_target_feature), &[]), + // tidy-alphabetical-end +]; + +#[rustfmt::skip] +const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + // For "backchain", https://github.com/rust-lang/rust/issues/142412 is a stabilization blocker + ("backchain", Unstable(sym::s390x_target_feature), &[]), + ("concurrent-functions", Unstable(sym::s390x_target_feature), &[]), + ("deflate-conversion", Unstable(sym::s390x_target_feature), &[]), + ("enhanced-sort", Unstable(sym::s390x_target_feature), &[]), + ("guarded-storage", Unstable(sym::s390x_target_feature), &[]), + ("high-word", Unstable(sym::s390x_target_feature), &[]), + // LLVM does not define message-security-assist-extension versions 1, 2, 6, 10 and 11. + ("message-security-assist-extension3", Unstable(sym::s390x_target_feature), &[]), + ("message-security-assist-extension4", Unstable(sym::s390x_target_feature), &[]), + ("message-security-assist-extension5", Unstable(sym::s390x_target_feature), &[]), + ("message-security-assist-extension8", Unstable(sym::s390x_target_feature), &["message-security-assist-extension3"]), + ("message-security-assist-extension9", Unstable(sym::s390x_target_feature), &["message-security-assist-extension3", "message-security-assist-extension4"]), + ("message-security-assist-extension12", Unstable(sym::s390x_target_feature), &[]), + ("miscellaneous-extensions-2", Stable, &[]), + ("miscellaneous-extensions-3", Stable, &[]), + ("miscellaneous-extensions-4", Stable, &[]), + ("nnp-assist", Stable, &["vector"]), + // Pinned down by [`Target::abi_required_features`]. + ("soft-float", InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, &[]), + ("transactional-execution", Unstable(sym::s390x_target_feature), &[]), + ("vector", Stable, &[]), + ("vector-enhancements-1", Stable, &["vector"]), + ("vector-enhancements-2", Stable, &["vector-enhancements-1"]), + ("vector-enhancements-3", Stable, &["vector-enhancements-2"]), + ("vector-packed-decimal", Stable, &["vector"]), + ("vector-packed-decimal-enhancement", Stable, &["vector-packed-decimal"]), + ("vector-packed-decimal-enhancement-2", Stable, &["vector-packed-decimal-enhancement"]), + ("vector-packed-decimal-enhancement-3", Stable, &["vector-packed-decimal-enhancement-2"]), + // tidy-alphabetical-end +]; + +const SPARC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("leoncasa", Unstable(sym::sparc_target_feature), &[]), + ( + "soft-float", + InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, + &[], + ), + ("v8plus", Unstable(sym::sparc_target_feature), &[]), + // FIXME: It's unclear what this feature means when `v8plus` is disabled on 32-bit SPARC. See + // the discussion around https://github.com/rust-lang/rust/pull/160949#discussion_r3806194355. + ("v9", Unstable(sym::sparc_target_feature), &[]), + // tidy-alphabetical-end +]; + +static M68K_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("isa-68000", Unstable(sym::m68k_target_feature), &[]), + ("isa-68010", Unstable(sym::m68k_target_feature), &["isa-68000"]), + ("isa-68020", Unstable(sym::m68k_target_feature), &["isa-68010"]), + ("isa-68030", Unstable(sym::m68k_target_feature), &["isa-68020"]), + ("isa-68040", Unstable(sym::m68k_target_feature), &["isa-68030", "isa-68882"]), + ("isa-68060", Unstable(sym::m68k_target_feature), &["isa-68040"]), + // FPU + ("isa-68881", Unstable(sym::m68k_target_feature), &[]), + ("isa-68882", Unstable(sym::m68k_target_feature), &["isa-68881"]), + // tidy-alphabetical-end +]; + +static AVR_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("addsubiw", Unstable(sym::avr_target_feature), &[]), + ("break", Unstable(sym::avr_target_feature), &[]), + ("eijmpcall", Unstable(sym::avr_target_feature), &[]), + ("elpm", Unstable(sym::avr_target_feature), &[]), + ("elpmx", Unstable(sym::avr_target_feature), &[]), + ("ijmpcall", Unstable(sym::avr_target_feature), &[]), + ("jmpcall", Unstable(sym::avr_target_feature), &[]), + ("lowbytefirst", Unstable(sym::avr_target_feature), &[]), + ("lpm", Unstable(sym::avr_target_feature), &[]), + ("lpmx", Unstable(sym::avr_target_feature), &[]), + ("movw", Unstable(sym::avr_target_feature), &[]), + ("mul", Unstable(sym::avr_target_feature), &[]), + ("rmw", Unstable(sym::avr_target_feature), &[]), + ("spm", Unstable(sym::avr_target_feature), &[]), + ("spmx", Unstable(sym::avr_target_feature), &[]), + ( + "sram", + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { reason: "devices that have no SRAM are unsupported", hard_error: false }, + &[], + ), + ("tinyencoding", Unstable(sym::avr_target_feature), &[]), + // tidy-alphabetical-end +]; + +const XTENSA_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + ("bool", Unstable(sym::xtensa_target_feature), &[]), + ("fp", Unstable(sym::xtensa_target_feature), &["bool", "coprocessor"]), + ("coprocessor", Unstable(sym::xtensa_target_feature), &[]), + ("highpriinterrupts", Unstable(sym::xtensa_target_feature), &["interrupt"]), + ("interrupt", Unstable(sym::xtensa_target_feature), &["exception"]), + ( + "windowed", + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { + reason: "windowed changes the Xtensa calling convention", + hard_error: false, + }, + &["exception"], + ), + ("loop", Unstable(sym::xtensa_target_feature), &[]), + ("sext", Unstable(sym::xtensa_target_feature), &[]), + ("nsa", Unstable(sym::xtensa_target_feature), &[]), + ("mul32", Unstable(sym::xtensa_target_feature), &[]), + ("mul32high", Unstable(sym::xtensa_target_feature), &["mul32"]), + ("div32", Unstable(sym::xtensa_target_feature), &[]), + ("mac16", Unstable(sym::xtensa_target_feature), &[]), + ("s32c1i", Unstable(sym::xtensa_target_feature), &[]), + ("threadptr", Unstable(sym::xtensa_target_feature), &[]), + ("extendedl32r", Unstable(sym::xtensa_target_feature), &[]), + ("debug", Unstable(sym::xtensa_target_feature), &["exception"]), + ("exception", Unstable(sym::xtensa_target_feature), &[]), + ("rvector", Unstable(sym::xtensa_target_feature), &["exception"]), + ("prid", Unstable(sym::xtensa_target_feature), &[]), + ("regprotect", Unstable(sym::xtensa_target_feature), &[]), + ("miscsr", Unstable(sym::xtensa_target_feature), &[]), +]; + +static TARGET_FEATURE_IMPLICATIONS_RAW: &[&[(&str, Stability, ImpliedFeatures)]] = &[ + ARM_FEATURES, + AARCH64_FEATURES, + X86_FEATURES, + HEXAGON_FEATURES, + POWERPC_FEATURES, + MIPS_FEATURES, + NVPTX_FEATURES, + RISCV_FEATURES, + WASM_FEATURES, + BPF_FEATURES, + XTENSA_FEATURES, + CSKY_FEATURES, + LOONGARCH_FEATURES, + IBMZ_FEATURES, + SPARC_FEATURES, + M68K_FEATURES, + AVR_FEATURES, ]; // spellchecker:on diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index 2e433e8be8909..3f95938255ded 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -13,16 +13,15 @@ mod simple; mod trait_aliases; mod traits; -use base_db::{Crate, SourceDatabase}; +use base_db::{Crate, EditionedFileId, SourceDatabase}; use expect_test::Expect; use hir_def::{ - AdtId, AssocItemId, DefWithBodyId, GenericDefId, HasModule, Lookup, ModuleDefId, ModuleId, + AdtId, AssocItemId, DefWithBodyId, GenericDefId, HasModule, ModuleDefId, ModuleId, SyntheticSyntax, VariantId, - expr_store::{Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap}, - hir::{ExprId, Pat, PatId}, + expr_store::{Body, ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr}, + hir::{BindingId, ClosureKind, Expr, ExprId, Pat, PatId}, item_scope::ItemScope, nameres::DefMap, - src::HasSource, type_ref::TypeRefId, }; use hir_expand::{FileRange, InFile}; @@ -36,7 +35,7 @@ use syntax::{ use test_fixture::WithFixture; use crate::{ - InferenceDiagnostic, InferenceResult, + InferBodyId, InferenceDiagnostic, InferenceResult, db::{AnonConstId, HirDatabase}, display::{DisplayTarget, HirDisplay}, infer::Adjustment, @@ -69,6 +68,115 @@ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) { check_impl(ra_fixture, false, false, false) } +fn infer_defs<'db>( + db: &'db TestDB, + files: &[EditionedFileId], +) -> Vec<( + InferBodyId<'db>, + &'db ExpressionStore, + &'db ExpressionStoreSourceMap, + ExprId, + Option<(BindingId, Option>)>, + Crate, +)> { + let mut defs = Vec::new(); + + let anon_const_data = |anon_const: AnonConstId<'db>, krate| { + let loc = anon_const.loc(db); + let (store, source_map) = ExpressionStore::with_source_map(db, loc.owner); + (InferBodyId::AnonConstId(anon_const), store, source_map, loc.expr, None, krate) + }; + let push_generic_def = |defs: &mut Vec<_>, generic_def: Option, krate| { + let Some(generic_def) = generic_def else { return }; + defs.extend( + AnonConstId::all_from_signature(db, generic_def).map(|it| anon_const_data(it, krate)), + ); + }; + let push_def_with_body = |defs: &mut Vec<_>, def_with_body: Option, krate| { + let Some(def_with_body) = def_with_body else { return }; + let (body, source_map) = Body::with_source_map(db, def_with_body); + + let mut root_expr = body.root_expr(); + if source_map.expr_syntax(root_expr).is_err() + && let Expr::Closure { + closure_kind: ClosureKind::Coroutine { .. }, + body: closure_body, + .. + } = body[root_expr] + && let Expr::Block { tail: Some(tail), .. } = body[closure_body] + { + // async fns etc. lower to coroutines without source as the root expr. + root_expr = tail; + } + + defs.push(( + InferBodyId::DefWithBodyId(def_with_body), + &***body, + &**source_map, + root_expr, + body.self_param.map(|param| (param.formal, source_map.self_param_syntax())), + krate, + )); + }; + + for file_id in files { + let module = db.module_for_file_opt(file_id.file_id(db)); + let module = match module { + Some(m) => m, + None => continue, + }; + let def_map = module.def_map(db); + let krate = module.krate(db); + + visit_module(db, def_map, module, &mut |it| { + let generic_def = match it { + ModuleDefId::FunctionId(it) => Some(it.into()), + ModuleDefId::AdtId(it) => Some(it.into()), + ModuleDefId::ConstId(it) => Some(it.into()), + ModuleDefId::StaticId(it) => Some(it.into()), + ModuleDefId::TraitId(it) => Some(it.into()), + ModuleDefId::TypeAliasId(it) => Some(it.into()), + ModuleDefId::ModuleId(_) => None, + ModuleDefId::EnumVariantId(_) => None, + ModuleDefId::BuiltinType(_) => None, + ModuleDefId::MacroId(_) => None, + }; + push_generic_def(&mut defs, generic_def, krate); + + let variant_def: Option = match it { + ModuleDefId::AdtId(AdtId::StructId(it)) => Some(it.into()), + ModuleDefId::AdtId(AdtId::UnionId(it)) => Some(it.into()), + ModuleDefId::EnumVariantId(it) => Some(it.into()), + _ => None, + }; + if let Some(variant_def) = variant_def { + defs.extend( + db.field_types_with_diagnostics(variant_def) + .defined_anon_consts() + .iter() + .map(|&it| anon_const_data(it, krate)), + ); + } + + let def_with_body = match it { + ModuleDefId::FunctionId(it) => Some(it.into()), + ModuleDefId::EnumVariantId(it) => Some(it.into()), + ModuleDefId::ConstId(it) => Some(it.into()), + ModuleDefId::StaticId(it) => Some(it.into()), + _ => None, + }; + push_def_with_body(&mut defs, def_with_body, krate); + }); + } + + defs.retain(|(_, _, source_map, root_expr, _, _)| source_map.expr_syntax(*root_expr).is_ok()); + defs.sort_by_key(|(_, _, source_map, root_expr, _, _)| { + source_map.expr_syntax(*root_expr).unwrap().value.text_range().start() + }); + + defs +} + #[track_caller] fn check_impl( #[rust_analyzer::rust_fixture] ra_fixture: &str, @@ -103,55 +211,18 @@ fn check_impl( } assert!(had_annotations || allow_none, "no `//^` annotations found"); - let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new(); - for file_id in files { - let module = db.module_for_file_opt(file_id.file_id(&db)); - let module = match module { - Some(m) => m, - None => continue, - }; - let def_map = module.def_map(&db); - visit_module(&db, def_map, module, &mut |it| { - let def = match it { - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::EnumVariantId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - ModuleDefId::StaticId(it) => it.into(), - _ => return, - }; - defs.push((def, module.krate(&db))) - }); - } - defs.sort_by_key(|(def, _)| match def { - DefWithBodyId::FunctionId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::ConstId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::StaticId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::VariantId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - }); + let defs = infer_defs(&db, &files); let mut unexpected_type_mismatches = String::new(); - for (def, krate) in defs { + for (def, store, source_map, _root_expr, _self_param, krate) in defs { let display_target = DisplayTarget::from_crate(&db, krate); - let (body, body_source_map) = Body::with_source_map(&db, def); let inference_result = InferenceResult::of(&db, def); for (pat, ty) in inference_result.type_of_pat.iter() { let mut ty = ty.as_ref(); - if let Pat::Bind { id, .. } = body[pat] { + if let Pat::Bind { id, .. } = store[pat] { ty = inference_result.type_of_binding[id].as_ref(); } - let node = match pat_node(body_source_map, pat, &db) { + let node = match pat_node(source_map, pat, &db) { Some(value) => value, None => continue, }; @@ -168,7 +239,7 @@ fn check_impl( for (expr, ty) in inference_result.type_of_expr.iter() { let ty = ty.as_ref(); - let node = match expr_node(body_source_map, expr, &db) { + let node = match expr_node(source_map, expr, &db) { Some(value) => value, None => continue, }; @@ -205,10 +276,8 @@ fn check_impl( }); for (expr_or_pat, expected, actual) in type_mismatches { let Some(node) = (match expr_or_pat.unpack() { - hir_def::hir::ExprOrPatId::ExprId(expr) => { - expr_node(body_source_map, expr, &db) - } - hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(body_source_map, pat, &db), + hir_def::hir::ExprOrPatId::ExprId(expr) => expr_node(source_map, expr, &db), + hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(source_map, pat, &db), }) else { continue; }; @@ -227,7 +296,7 @@ fn check_impl( } for (type_ref, ty) in inference_result.placeholder_types() { - let node = match type_node(body_source_map, type_ref, &db) { + let node = match type_node(source_map, type_ref, &db) { Some(value) => value, None => continue, }; @@ -272,11 +341,11 @@ fn check_impl( } fn expr_node( - body_source_map: &BodySourceMap, + source_map: &ExpressionStoreSourceMap, expr: ExprId, db: &TestDB, ) -> Option> { - Some(match body_source_map.expr_syntax(expr) { + Some(match source_map.expr_syntax(expr) { Ok(sp) => { let root = sp.file_id.parse_or_expand(db); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) @@ -286,11 +355,11 @@ fn expr_node( } fn pat_node( - body_source_map: &BodySourceMap, + source_map: &ExpressionStoreSourceMap, pat: PatId, db: &TestDB, ) -> Option> { - Some(match body_source_map.pat_syntax(pat) { + Some(match source_map.pat_syntax(pat) { Ok(sp) => { let root = sp.file_id.parse_or_expand(db); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) @@ -300,11 +369,11 @@ fn pat_node( } fn type_node( - body_source_map: &BodySourceMap, + source_map: &ExpressionStoreSourceMap, type_ref: TypeRefId, db: &TestDB, ) -> Option> { - Some(match body_source_map.type_syntax(type_ref) { + Some(match source_map.type_syntax(type_ref) { Ok(sp) => { let root = sp.file_id.parse_or_expand(db); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) @@ -327,10 +396,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let mut infer_def = |inference_result: &InferenceResult<'_>, store: &ExpressionStore, source_map: &ExpressionStoreSourceMap, - self_param: Option<( - hir_def::hir::BindingId, - Option>, - )>, + self_param: Option<(BindingId, Option>)>, krate: Crate| { let display_target = DisplayTarget::from_crate(&db, krate); let mut types: Vec<(InFile, Ty<'_>)> = Vec::new(); @@ -428,122 +494,10 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { } }; - let module = db.module_for_file(file_id.file_id(&db)); - let def_map = module.def_map(&db); - - let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new(); - let mut generic_defs: Vec<(GenericDefId, Crate)> = Vec::new(); - let mut variants: Vec<(VariantId, Crate)> = Vec::new(); - visit_module(&db, def_map, module, &mut |it| { - let krate = module.krate(&db); - match it { - ModuleDefId::FunctionId(it) => { - defs.push((it.into(), krate)); - generic_defs.push((it.into(), krate)); - } - ModuleDefId::EnumVariantId(it) => { - defs.push((it.into(), krate)); - } - ModuleDefId::ConstId(it) => { - defs.push((it.into(), krate)); - generic_defs.push((it.into(), krate)); - } - ModuleDefId::StaticId(it) => { - defs.push((it.into(), krate)); - generic_defs.push((it.into(), krate)); - } - ModuleDefId::AdtId(it) => { - generic_defs.push((it.into(), krate)); - match it { - AdtId::StructId(id) => variants.push((id.into(), krate)), - AdtId::UnionId(id) => variants.push((id.into(), krate)), - AdtId::EnumId(id) => variants.extend( - id.enum_variants(&db) - .variants - .values() - .map(|&(variant, ..)| (variant.into(), krate)), - ), - } - } - ModuleDefId::TraitId(it) => { - generic_defs.push((it.into(), krate)); - } - ModuleDefId::TypeAliasId(it) => { - generic_defs.push((it.into(), krate)); - } - _ => {} - } - }); - // Also collect impls - for impl_id in def_map[module].scope.impls() { - generic_defs.push((impl_id.into(), module.krate(&db))); - let impl_data = impl_id.impl_items(&db); - for &(_, item) in impl_data.items.iter() { - match item { - AssocItemId::FunctionId(it) => { - generic_defs.push((it.into(), module.krate(&db))); - } - AssocItemId::ConstId(it) => { - generic_defs.push((it.into(), module.krate(&db))); - } - AssocItemId::TypeAliasId(it) => { - generic_defs.push((it.into(), module.krate(&db))); - } - } - } - } - - defs.sort_by_key(|(def, _)| match def { - DefWithBodyId::FunctionId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::ConstId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::StaticId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - DefWithBodyId::VariantId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.syntax().text_range().start() - } - }); - for (def, krate) in defs { - let (body, source_map) = Body::with_source_map(&db, def); + let defs = infer_defs(&db, &[file_id]); + for (def, store, source_map, _root_expr, self_param, krate) in defs { let infer = InferenceResult::of(&db, def); - let self_param = - body.self_param.map(|param| (param.formal, source_map.self_param_syntax())); - infer_def(infer, body, source_map, self_param, krate); - } - - // Also infer signature const expressions (array lengths, const generic args, etc.) - generic_defs.dedup(); - for (def, krate) in generic_defs { - let (store, source_map) = ExpressionStore::with_source_map(&db, def.into()); - // Skip if there are no const expressions in the signature - if store.expr_roots().next().is_none() { - continue; - } - for anon_const in AnonConstId::all_from_signature(&db, def) { - let infer = InferenceResult::of(&db, anon_const); - infer_def(infer, store, source_map, None, krate); - } - } - variants.dedup(); - for (def, krate) in variants { - let (store, source_map) = ExpressionStore::with_source_map(&db, def.into()); - // Skip if there are no const expressions in the signature - if store.expr_roots().next().is_none() { - continue; - } - let anon_consts = db.field_types_with_diagnostics(def).defined_anon_consts(); - for &anon_const in anon_consts { - let infer = InferenceResult::of(&db, anon_const); - infer_def(infer, store, source_map, None, krate); - } + infer_def(infer, store, source_map, self_param, krate); } buf.truncate(buf.trim_end().len()); 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 6d4ad930702df..058bc8e233ec9 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 @@ -536,6 +536,40 @@ fn test() { ); } +#[test] +fn coerce_fn_item_with_unsafe_in_array() { + check_no_mismatches( + r" +fn foo(x: u32) -> isize { 1 } +unsafe fn bar(x: u32) -> isize { 1 } +fn test() { + let f = [foo, bar]; + // ^^^ adjustments: Pointer(ReifyFnPointer) +}", + ); +} + +#[test] +fn coerce_fn_items_with_unsafe_in_match_arms() { + check_no_mismatches( + r" +const fn foo1(x: u32) -> isize { 1 } +unsafe fn foo2(x: u32) -> isize { 2 } +fn foo3(x: u32) -> isize { 3 } +fn test() { + let x = match 1 { + 1 => foo1, + // ^^^^ adjustments: Pointer(ReifyFnPointer) + 2 => foo2, + // ^^^^ adjustments: Pointer(ReifyFnPointer) + _ => foo3, + // ^^^^ adjustments: Pointer(ReifyFnPointer), Pointer(UnsafeFnPointer) + }; + x; +}", + ); +} + #[test] fn coerce_closure_to_fn_ptr() { check_no_mismatches( @@ -1087,3 +1121,30 @@ async fn foo(a: &[i32]) -> &[i32] { "#, ); } + +#[test] +fn coerce_unsize_infer_var_via_default_and_box() { + // Regression for rust-analyzer#23114: `y` is only related to the `Sized` + // obligation through subtyping (`Box::new(y)` then unsize to `Box`). + check_no_mismatches( + r#" +//- minicore: coerce_unsized, send, default +use core::{marker::Unsize, ops::CoerceUnsized}; + +struct Box(*const T); +impl, U: ?Sized> CoerceUnsized> for Box {} +impl Box { + fn new(t: T) -> Self { + loop {} + } +} + +fn main() { + let y = Default::default(); + let x = Box::new(y); + let z: Box = x; + let _: i32 = y; +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 985ecb6c5d3b2..2300923a659bc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -660,7 +660,6 @@ fn infer_call_trait_method_on_generic_param_1() { } "#, expect![[r#" - 29..33 'self': &'? Self 63..64 't': T 69..88 '{ ...d(); }': () 75..76 't': T @@ -681,7 +680,6 @@ fn infer_call_trait_method_on_generic_param_2() { } "#, expect![[r#" - 32..36 'self': &'? Self 70..71 't': T 76..95 '{ ...d(); }': () 82..83 't': T @@ -707,7 +705,6 @@ fn infer_with_multiple_trait_impls() { } "#, expect![[r#" - 28..32 'self': Self 110..201 '{ ...(S); }': () 120..121 'x': u32 129..130 'S': S @@ -1041,7 +1038,6 @@ fn super_trait_impl_return_trait_method_resolution() { } "#, expect![[r#" - 24..28 'self': Self 90..101 '{ loop {} }': ! 92..99 'loop {}': ! 97..99 '{}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index e53f7503dac6b..b4b7caa1c8bee 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -954,3 +954,14 @@ fn test() -> i32 { "#, ); } + +#[test] +fn diverging_let_with_else() { + check_no_mismatches( + r#" +fn x() { + let true: bool = return else {}; +} + "#, + ); +} 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 a6c8b4ac6a271..e3b17d1274d9e 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 @@ -658,8 +658,6 @@ fn issue_4053_diesel_where_clauses() { } "#, expect![[r#" - 65..69 'self': Self - 267..271 'self': Self 466..470 'self': SelectStatement 488..522 '{ ... }': () 498..502 'self': SelectStatement @@ -720,7 +718,6 @@ fn issue_4931() { } "#, expect![[r#" - 117..121 'self': Self 148..149 'i': T 154..170 '{ ...w(); }': () 160..161 'i': T @@ -811,7 +808,6 @@ fn issue_4800() { 401..424 '{ ... }': dyn Future + 'static 411..418 'loop {}': ! 416..418 '{}': () - 575..579 'self': &'? mut Self "#]], ); } @@ -1030,7 +1026,6 @@ fn test() { } "#, expect![[r#" - 176..184 'residual': R 418..419 'r': ControlFlow 448..476 '{ Cont...ata) }': ControlFlow 450..461 'ControlFlow': fn ControlFlow(PhantomData<(B, C)>) -> ControlFlow @@ -2776,8 +2771,6 @@ where 319..323 'None': Option 340..343 '&()': &'? () 341..343 '()': () - 421..425 'self': &'? Self - 427..433 'filter': &'? Filter<'?, '?, T> 580..584 'self': &'? [T; N] 586..592 'filter': &'? Filter<'?, '?, T> 622..704 '{ ... }': T diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 59563bdc33eaa..2b64ba88b1372 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -582,6 +582,8 @@ fn test_at_most() { 200..206 '_other': Between 222..242 '{ ... }': Between 232..236 'self': Between + 320..335 '{ Consts::MAX }': usize + 322..333 'Consts::MAX': usize 300..304 'self': Self 343..372 '{ ... }': Between 353..360 'Between': fn Between(Self) -> Between @@ -607,10 +609,6 @@ fn test_at_most() { 623..626 ''9'': char 623..641 ''9'.at...:<1>()': Between<0, 1, char> 637..638 '1': usize - 320..335 '{ Consts::MAX }': usize - 322..333 'Consts::MAX': usize - 144..159 '{ Consts::MAX }': usize - 146..157 'Consts::MAX': usize "#]], ); } @@ -669,6 +667,40 @@ where ); } +#[test] +fn opaque_iterator_uses_blanket_into_iterator_impl() { + check_types( + r#" +//- minicore: iterator +struct Item; +impl Item { + fn method(&self) -> usize { + 0 + } +} + +struct Iter; +impl Iterator for Iter { + type Item = Item; + + fn next(&mut self) -> Option { + None + } +} + +fn iterator(recurse: bool) -> impl Iterator { + if recurse { + for item in iterator(false) { + item.method(); + //^^^^^^^^^^^^^ usize + } + } + Iter +} +"#, + ); +} + #[test] fn regression_16282() { check_infer( @@ -740,7 +772,6 @@ where } "#, expect![[r#" - 43..47 'self': &'? Self 168..172 'self': &'? F 205..227 '{ ... }': >::CallRefFuture<'?> 215..219 'self': &'? F @@ -749,6 +780,20 @@ where ); } +#[test] +fn async_closure_hrtb_return_type() { + check_no_mismatches( + r#" +//- minicore: async_fn +fn take(_: impl for<'s> AsyncFn(&'s ()) -> &'s ()) {} + +fn f() { + take(async |s| s); +} +"#, + ); +} + #[test] fn regression_19957() { // This test documents issue #19957: async-trait patterns incorrectly produce @@ -864,7 +909,6 @@ fn main() { 192..224 '{ ... }': Option 202..206 'self': &'? mut Foo 202..218 'self.n...spec()': Option - 278..282 'self': &'? mut Self 380..384 'self': &'? mut Foo 408..428 '{ ... }': Option 418..422 'None': Option @@ -924,10 +968,6 @@ fn test2(factory: T) { } "#, expect![[r#" - 39..43 'self': &'? Self - 101..105 'self': &'? Self - 198..202 'self': &'? Self - 239..243 'self': &'? Self 290..293 'foo': impl Foo + ?Sized 325..359 '{ ...z(); }': () 335..338 'baz': u8 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 0a6250c450f8b..89c0c6c4dc10a 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 @@ -2908,7 +2908,6 @@ struct Astruct; impl B for Astruct {} "#, expect![[r#" - 428..430 '_x': T 733..737 'self': Box<[T], A> 766..798 '{ ... }': Vec 812..940 '{ ...])); }': () @@ -3260,7 +3259,6 @@ fn main() { } "#, expect![[r#" - 104..108 'self': &'? Box 188..192 'self': &'_ Box> 218..220 '{}': &'? T 242..246 'self': &'_ Box> @@ -4296,10 +4294,10 @@ union U { } "#, expect![[r#" - 242..243 '0': isize 111..125 '{ C as usize }': usize 113..114 'C': f32 113..123 'C as usize': usize + 242..243 '0': isize "#]], ); } @@ -4464,3 +4462,15 @@ where "#, ); } + +#[test] +fn anon_consts_generic_params() { + check_types( + r#" +struct Foo { + a: usize = { {}; N }, + // ^ usize +} + "#, + ); +} 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 2c107da42ee9a..dc746c75226a1 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 @@ -1022,8 +1022,6 @@ fn test(x: impl Trait, y: &impl Trait) { z.foo2(); }"#, expect![[r#" - 29..33 'self': &'? Self - 54..58 'self': &'? Self 77..78 'x': impl Trait 97..99 '{}': () 154..155 'x': impl Trait @@ -1220,8 +1218,6 @@ fn test(x: impl Trait, y: &impl Trait) { z.foo2(); }"#, expect![[r#" - 29..33 'self': &'? Self - 54..58 'self': &'? Self 98..100 '{}': () 110..111 'x': impl Trait 130..131 'y': &'? impl Trait @@ -1344,7 +1340,6 @@ fn test() { a.foo(); }"#, expect![[r#" - 29..33 'self': &'? Self 71..82 '{ loop {} }': ! 73..80 'loop {}': ! 78..80 '{}': () @@ -1382,8 +1377,6 @@ fn test() { d.foo(); }"#, expect![[r#" - 49..53 'self': &'? mut Self - 101..105 'self': &'? Self 184..195 '{ loop {} }': (impl Iterator>, impl Trait) 186..193 'loop {}': ! 191..193 '{}': () @@ -1488,8 +1481,6 @@ fn test(x: Box>, y: &dyn Trait) { z.foo2(); }"#, expect![[r#" - 29..33 'self': &'? Self - 54..58 'self': &'? Self 206..208 '{}': Box + '?> 218..219 'x': Box + 'static> 242..243 'y': &'? (dyn Trait + 'static) @@ -1536,12 +1527,10 @@ fn test(s: S) { s.bar().baz(); }"#, expect![[r#" - 32..36 'self': &'? Self 106..110 'self': &'? S 132..143 '{ loop {} }': &'? (dyn Trait + 'static) 134..141 'loop {}': ! 139..141 '{}': () - 179..183 'self': &'? Self 255..256 's': S 271..293 '{ ...z(); }': () 277..278 's': S @@ -1570,7 +1559,6 @@ fn test(x: Trait, y: &Trait) -> u64 { z.foo(); }"#, expect![[r#" - 26..30 'self': &'? Self 60..62 '{}': dyn Trait + '? 72..73 'x': dyn Trait + 'static 82..83 'y': &'? (dyn Trait + 'static) @@ -1800,7 +1788,6 @@ fn test>(x: T) { x.foo(); }"#, expect![[r#" - 61..65 'self': Self 163..164 'x': T 169..185 '{ ...o(); }': () 175..176 'x': T @@ -1992,7 +1979,6 @@ fn test() { opt.map(f); }"#, expect![[r#" - 28..32 'self': &'? Self 132..136 'self': &'? Bar 149..160 '{ loop {} }': (A1, R) 151..158 'loop {}': ! @@ -2186,7 +2172,6 @@ fn test() { 112..123 '{ loop {} }': U 114..121 'loop {}': ! 119..121 '{}': () - 158..162 'self': S 210..214 'self': S 216..217 'x': T 222..223 'f': F @@ -2354,8 +2339,6 @@ impl Trait for S2 { fn f(&self, x: ::Item) { let y = x; } }"#, expect![[r#" - 40..44 'self': &'? Self - 46..47 'x': ::Item 126..130 'self': &'? S 132..133 'x': u32 147..161 '{ let y = x; }': () @@ -2914,7 +2897,6 @@ fn main() { 240..251 '{ loop {} }': () 242..249 'loop {}': ! 247..249 '{}': () - 360..364 'self': Self 692..696 'self': I 703..723 '{ ... }': I 713..717 'self': I @@ -3022,7 +3004,6 @@ fn test() { (IsCopy, NotCopy).test(); }"#, expect![[r#" - 78..82 'self': &'? Self 134..235 '{ ...t(); }': () 140..146 'IsCopy': IsCopy 140..153 'IsCopy.test()': bool @@ -3064,7 +3045,6 @@ fn test() { 28..29 'T': {unknown} 36..38 '{}': T 36..38: expected T, got () - 113..117 'self': &'? Self 169..249 '{ ...t(); }': () 175..178 'foo': fn foo() 175..185 'foo.test()': bool @@ -3092,7 +3072,6 @@ fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) { f3.test(); }"#, expect![[r#" - 22..26 'self': &'? Self 76..78 'f1': fn() 86..88 'f2': fn(usize) -> u8 107..109 'f3': fn(u8, u8) -> &'? u8 @@ -3122,7 +3101,6 @@ fn test() { (1u8, *"foo").test(); // not Sized }"#, expect![[r#" - 22..26 'self': &'? Self 79..194 '{ ...ized }': () 85..88 '1u8': u8 85..95 '1u8.test()': bool @@ -3504,8 +3482,6 @@ fn test() { let r = x.do_op(y); }"#, expect![[r#" - 63..67 'self': Self - 69..72 'rhs': RHS 153..157 'self': A 159..162 'rhs': A 186..206 '{ ... }': bool @@ -3779,7 +3755,6 @@ fn main() { } "#, expect![[r#" - 44..48 'self': &'? Self 133..137 'self': &'? [u8; 4] 155..172 '{ ... }': usize 165..166 '2': usize @@ -3827,7 +3802,6 @@ fn main() { } "#, expect![[r#" - 44..48 'self': &'? Self 151..155 'self': &'? [u8; L] 173..194 '{ ... }': [u8; L] 183..188 '*self': [u8; L] @@ -3879,7 +3853,6 @@ impl foo::Foo for u32 { } "#, expect![[r#" - 45..49 'self': Self 67..71 'self': Self 82..87 '{ 0 }': usize 84..85 '0': usize @@ -4316,7 +4289,6 @@ fn f<'a>(v: &dyn Trait = &'a i32>) { } "#, expect![[r#" - 90..94 'self': &'? Self 127..128 'v': &'? (dyn Trait = &'_ i32> + 'static) 164..195 '{ ...f(); }': () 170..171 'v': &'? (dyn Trait = &'_ i32> + 'static) @@ -5411,3 +5383,26 @@ fn probe(h: &Holder) { "#, ); } + +#[test] +fn assoc_type_bounds_on_trait() { + check_types( + r#" +trait Trait { + fn method(&self) -> i32; +} + +trait Foo +where + Self::Assoc: Trait, +{ + type Assoc; +} + +fn foo(v: T::Assoc) { + v.method(); + // ^^^^^^^^^^ i32 +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index cce1b4caf44a7..2594c52977a69 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -25,8 +25,8 @@ use crate::{ db::HirDatabase, generics::{Generics, generics}, next_solver::{ - Const, ConstKind, DbInterner, ExistentialPredicate, GenericArgKind, GenericArgs, Pattern, - PatternKind, Region, RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, + Const, ConstKind, ExistentialPredicate, GenericArgKind, GenericArgs, Pattern, PatternKind, + Region, RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, }, }; @@ -106,11 +106,10 @@ pub(crate) fn variances_of_cycle_initial( _: salsa::Id, def: GenericDefId, ) -> StoredVariancesOf { - let interner = DbInterner::new_no_crate(db); let generics = generics(db, def); let count = generics.len(true); - VariancesOf::new_from_iter(interner, std::iter::repeat_n(Variance::Bivariant, count)).store() + VariancesOf::new_from_iter(std::iter::repeat_n(Variance::Bivariant, count)).store() } struct Context<'db> { @@ -513,7 +512,7 @@ struct Foo { //~ ERROR [T: o] } "#, expect![[r#" - Foo[T: invariant] + Foo[T: bivariant] "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 61eda80fb4877..d8e3c37e69fff 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -46,7 +46,8 @@ fn write_builtin_derive_impl_method<'db>( let loc = impl_.loc(db); let adt_params = GenericParams::of(db, loc.adt.into()); - if f.show_container_bounds() && !adt_params.is_empty() { + // Builtin derive impl always with a trait + if f.show_container_bounds() { f.write_str("impl")?; write_generic_params(loc.adt.into(), f)?; f.write_char(' ')?; @@ -115,7 +116,8 @@ impl<'db> HirDisplay<'db> for Function { } ItemContainerId::ImplId(impl_) => { let (params, params_store) = GenericParams::with_store(f.db, impl_.into()); - if f.show_container_bounds() && !params.is_empty() { + let with_trait = ImplSignature::of(db, impl_).target_trait.is_some(); + if f.show_container_bounds() && (!params.is_empty() || with_trait) { write_impl_header(impl_, f)?; f.write_char('\n')?; has_disaplayable_predicates(f.db, params, params_store).then_some(( @@ -383,6 +385,9 @@ impl<'db> HirDisplay<'db> for Struct { f.write_char(')')?; write_where_clause(def_id, f)?; + if f.entity_limit.is_some() { + f.write_char(';')?; + } } StructKind::Record => { let has_where_clause = write_where_clause(def_id, f)?; @@ -391,7 +396,12 @@ impl<'db> HirDisplay<'db> for Struct { write_fields(&fields, hidden_fields, has_where_clause, limit, false, f)?; } } - StructKind::Unit => _ = write_where_clause(def_id, f)?, + StructKind::Unit => { + write_where_clause(def_id, f)?; + if f.entity_limit.is_some() { + f.write_char(';')?; + } + } } Ok(()) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index f9073138e32dd..eaf52e5dfe3ed 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3138,7 +3138,10 @@ impl<'db> GenericSubstitution<'db> { } } - pub fn types(&self, db: &'db dyn HirDatabase) -> Vec<(Symbol, Type<'db>)> { + fn types_impl( + &self, + db: &'db dyn HirDatabase, + ) -> impl Iterator, Option)> { let container = match self.def { GenericDefId::ConstId(id) => Some(id.lookup(db).container), GenericDefId::FunctionId(id) => Some(id.lookup(db).container), @@ -3165,9 +3168,12 @@ impl<'db> GenericSubstitution<'db> { TypeOrConstParamData::TypeParamData(param) => Some(param.name.clone()), TypeOrConstParamData::ConstParamData(_) => None, }); - self.subst - .types() - .zip(container_type_params.into_iter().flatten().chain(type_params)) + self.subst.types().zip(container_type_params.into_iter().flatten().chain(type_params)) + } + + /// Returns type arguments with type parameter names. + pub fn types(&self, db: &'db dyn HirDatabase) -> Vec<(Symbol, Type<'db>)> { + self.types_impl(db) .filter_map(|(ty, name)| { Some(( name?.symbol().clone(), @@ -3176,6 +3182,18 @@ impl<'db> GenericSubstitution<'db> { }) .collect() } + + /// Returns all type arguments, including unnamed ones such as implicit `impl Trait` parameters. + /// + /// Unlike [`Self::types`], this does not filter out arguments that have no + /// corresponding type parameter name. + // Note: this method is not used by rust-analyzer itself as of writing, but is + // used by external tools, so please do not remove it even if it appears unused. + pub fn all_types(&self, db: &'db dyn HirDatabase) -> Vec> { + self.types_impl(db) + .map(|(ty, _)| Type { ty: EarlyBinder::bind(ty), owner: self.owner }) + .collect() + } } /// A single local definition. @@ -5840,7 +5858,7 @@ impl<'db> Callable<'db> { } } - fn as_function(&self) -> Option { + pub fn as_function(&self) -> Option { match self.callee { Callee::Def(CallableDefId::FunctionId(it)) => Some(it.into()), Callee::BuiltinDeriveImplMethod { method, impl_ } => { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 49055172a1552..b30815bbb5607 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1634,6 +1634,9 @@ impl<'db> SemanticsImpl<'db> { let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file); expansion_info.arg().map(|node| node?.parent()).transpose() }) + .inspect(|node| { + self.cache(node.value.tree_top(), node.file_id); + }) } }) } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 368e5e3c147fd..0bcde7e4ed604 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -470,6 +470,7 @@ impl<'db> SourceAnalyzer<'db> { LifetimeElisionKind::Infer, LifetimeLoweringMode::LateParam, ) + .with_interning_mode(LoweringMode::Ide) .with_infer_vars_behavior(Some(&mut vars_cts)) .lower_ty(type_ref); @@ -1893,6 +1894,7 @@ fn resolve_hir_path_<'db>( LifetimeElisionKind::Infer, LifetimeLoweringMode::LateParam, ) + .with_interning_mode(LoweringMode::Ide) .lower_ty_ext(type_ref); res.map(|ty_ns| (ty_ns, path.segments().first(), Visibility::Public)) }), @@ -2097,6 +2099,7 @@ fn resolve_hir_path_qualifier<'db>( LifetimeElisionKind::Infer, LifetimeLoweringMode::LateParam, ) + .with_interning_mode(LoweringMode::Ide) .lower_ty_ext(type_ref); res.map(|ty_ns| (ty_ns, path.segments().first())) }), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs index 53205910c8e35..748e325f0e03c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -78,7 +78,13 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Definition::Function(it) => it, _ => return None, }; - let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); + let generics = hir::GenericDef::Function(fun) + .params(ctx.sema.db) + .into_iter() + .filter(|param| { + matches!(param, hir::GenericParam::TypeParam(_) | hir::GenericParam::ConstParam(_)) + }) + .collect::>(); if generics.is_empty() { cov_mark::hit!(add_turbo_fish_non_generic); return None; @@ -128,13 +134,6 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> } } - let number_of_arguments = generics - .iter() - .filter(|param| { - matches!(param, hir::GenericParam::TypeParam(_) | hir::GenericParam::ConstParam(_)) - }) - .count(); - acc.add( AssistId::refactor_rewrite("add_turbo_fish"), "Add `::<>`", @@ -146,7 +145,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Either::Right(it) => builder.make_editor(it.syntax()), }; - let fish_head = get_fish_head(editor.make(), number_of_arguments); + let fish_head = get_fish_head(editor.make(), generics.len()); match turbofish_target { Either::Left(path_segment) => { @@ -316,7 +315,7 @@ fn main() { #[test] fn add_turbo_fish_non_generic() { - cov_mark::check!(add_turbo_fish_non_generic); + cov_mark::check_count!(add_turbo_fish_non_generic, 2); check_assist_not_applicable( add_turbo_fish, r#" @@ -324,6 +323,15 @@ fn make() -> () {} fn main() { make$0(); } +"#, + ); + check_assist_not_applicable( + add_turbo_fish, + r#" +fn make<'a>() {} +fn main() { + make$0(); +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 65c1c7cb70e78..790f1fbeef19e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -230,6 +230,7 @@ fn remove_mut_and_collect_idents( | ast::Pat::PathPat(_) | ast::Pat::WildcardPat(_) | ast::Pat::NotNull(_) + | ast::Pat::CfgPredPat(_) | ast::Pat::ConstBlockPat(_) => pat.clone(), // don't support macro pat yet ast::Pat::MacroPat(_) => return None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index 9ff07d8dd6d2c..5ccae140d79d5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -354,6 +354,16 @@ impl Module { self.expand_and_group_usages_file_wise(ctx,replace_range, Definition::Macro(nod), &mut refs, &mut use_stmts_to_be_inserted); } }, + ast::Module(it) => { + if let Some(nod) = ctx.sema.to_def(&it) { + self.expand_and_group_usages_file_wise(ctx,replace_range, Definition::Module(nod), &mut refs, &mut use_stmts_to_be_inserted); + } + }, + ast::Trait(it) => { + if let Some(nod) = ctx.sema.to_def(&it) { + self.expand_and_group_usages_file_wise(ctx,replace_range, Definition::Trait(nod), &mut refs, &mut use_stmts_to_be_inserted); + } + }, _ => (), } } @@ -1328,6 +1338,7 @@ mod modname { extract_module, r" mod impl_play2 { +impl impl_play::A {} $0mod impl_play { pub struct A {} }$0 @@ -1335,6 +1346,7 @@ $0mod impl_play { ", r" mod impl_play2 { +impl modname::impl_play::A {} mod modname { pub(crate) mod impl_play { pub struct A {} @@ -1410,6 +1422,23 @@ modname::m! {} ); } + #[test] + fn test_extract_module_trait() { + check_assist( + extract_module, + r" +$0trait Foo {}$0 +impl Foo for () {} + ", + r" +mod modname { + pub(crate) trait Foo {} +} +impl modname::Foo for () {} + ", + ); + } + #[test] fn test_do_not_apply_visibility_modifier_to_trait_impl_items() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs index 73837f486cb30..739cc546e11f9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs @@ -47,10 +47,13 @@ pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_, ' return None; } - let Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e)))) = - ctx.sema.resolve_path(&path.qualifier()?) - else { - return None; + let e = match ctx.sema.resolve_path(&path.qualifier()?)? { + hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => e, + hir::PathResolution::SelfType(impl_) => { + let hir::Adt::Enum(e) = impl_.self_ty(ctx.db()).as_adt()? else { return None }; + e + } + _ => return None, }; let target = path.syntax().text_range(); @@ -528,6 +531,31 @@ fn foo(x: Foo) { Foo::Bar => } } +", + ) + } + + #[test] + fn generate_variant_for_self_in_impl() { + check_assist( + generate_enum_variant, + r" +enum Foo {} +impl Foo { + fn bar() { + Self::Baz$0 + } +} +", + r" +enum Foo { + Baz, +} +impl Foo { + fn bar() { + Self::Baz + } +} ", ) } 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 13096c6efc37a..46aa6e8f5c110 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 @@ -126,6 +126,11 @@ fn fn_target_info( } Some(hir::PathResolution::SelfType(impl_)) => { let adt = impl_.self_ty(ctx.db()).as_adt()?; + if let hir::Adt::Enum(_) = adt + && fn_name.starts_with(char::is_uppercase) + { + return None; + } assoc_fn_target_info(ctx, call, adt, fn_name) } _ => None, @@ -3462,4 +3467,44 @@ impl Foo for Bar { ", ) } + #[test] + fn not_applicable_for_self_enum_variant() { + check_assist_not_applicable( + generate_function, + r" +enum Foo {} +impl Foo { + fn foo() { + Self::Bar$0(true) + } +} +", + ); + } + #[test] + fn applicable_for_self_enum_method() { + check_assist( + generate_function, + r" +enum Foo {} +impl Foo { + fn foo() { + Self::bar$0() + } +} +", + r" +enum Foo {} +impl Foo { + fn foo() { + Self::bar() + } + + fn bar() { + ${0:todo!()} + } +} +", + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index 621ac9223abd9..06a2627bdb09e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -142,9 +142,10 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> let replaced = call_infos .into_iter() .map(|call_info| { + let indent = call_info.node.indent_level(); let replacement = inline( &ctx.sema, def_file, function, &func_body, ¶ms, &call_info, - editor, + editor, indent, ); editor.replace(call_info.node.syntax(), replacement.syntax()); }) @@ -219,7 +220,7 @@ pub(super) fn split_refs_and_uses( // } // ``` pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { - let name_ref: ast::NameRef = ctx.find_node_at_offset()?; + let name_ref: ast::NameRef = ctx.find_node_at_offset_with_descend()?; let call_info = CallInfo::from_name_ref( name_ref.clone(), ctx.sema.file_to_module_def(ctx.vfs_file_id())?.krate(ctx.db()).into(), @@ -250,8 +251,10 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt cov_mark::hit!(inline_call_recursive); return None; } - let syntax = call_info.node.syntax().clone(); - let editor = SyntaxEditor::new(syntax.tree_top()).0; + let original = ctx.sema.original_range_opt(call_info.node.syntax())?.range; + let edit_range = crate::utils::cover_edit_range(ctx.source_file().syntax(), original); + let editor = SyntaxEditor::new(ctx.source_file().syntax().clone()).0; + let text_range = edit_range.start().text_range().cover(edit_range.end().text_range()); let params = get_fn_params(ctx.sema.db, function, ¶m_list, editor.make())?; if call_info.arguments.len() != params.len() { @@ -261,10 +264,18 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt return None; } - acc.add(AssistId::refactor_inline("inline_call"), label, syntax.text_range(), |builder| { - let replacement = - inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info, &editor); - editor.replace(call_info.node.syntax(), replacement.syntax()); + acc.add(AssistId::refactor_inline("inline_call"), label, text_range, |builder| { + let replacement = inline( + &ctx.sema, + file_id, + function, + &fn_body, + ¶ms, + &call_info, + &editor, + IndentLevel::from_element(edit_range.start()), + ); + editor.replace_all(edit_range, vec![replacement.syntax().clone().into()]); builder.add_file_edits(ctx.vfs_file_id(), editor); }) } @@ -343,6 +354,7 @@ fn inline<'db>( params: &[(ast::Pat, Option, hir::Param<'db>)], CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo, file_editor: &SyntaxEditor, + original_indentation: IndentLevel, ) -> ast::Expr { let make = file_editor.make(); let file_id = sema.hir_file_for(fn_body.syntax()); @@ -629,10 +641,6 @@ fn inline<'db>( original_body_indent = IndentLevel(0); } - let original_indentation = match node { - ast::CallableExpr::Call(it) => it.indent_level(), - ast::CallableExpr::MethodCall(it) => it.indent_level(), - }; body = body.dedent(original_body_indent).indent(original_indentation); let no_stmts = body.statements().next().is_none(); @@ -769,6 +777,46 @@ fn main() { ); } + #[test] + fn call_inside_macro() { + check_assist( + inline_call, + r#" +macro_rules! identity { ($($t:tt)*) => { $($t)* }; } +fn foo(a: u32, b: u32) -> u32 { + let x = a + b; + let y = x - b; + x * y +} + +fn main() { + identity! { + let x = foo$0(1, 2); + } +} +"#, + r#" +macro_rules! identity { ($($t:tt)*) => { $($t)* }; } +fn foo(a: u32, b: u32) -> u32 { + let x = a + b; + let y = x - b; + x * y +} + +fn main() { + identity! { + let x = { + let b = 2; + let x = 1 + b; + let y = x - b; + x * y + }; + } +} +"#, + ); + } + #[test] fn function_with_self_param() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs index 427fbbeaa0e9f..0d4cbda9fcac2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs @@ -191,6 +191,15 @@ fn foo< ); } + #[test] + fn replace_impl_long_path() { + check_assist( + introduce_named_type_parameter, + r#"fn foo(bar: $0impl std::iter::Iterator) {}"#, + r#"fn foo<$0I: std::iter::Iterator>(bar: I) {}"#, + ); + } + #[test] fn replace_impl_with_mut() { 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 8acb887133e0f..49f481d32f39c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -432,6 +432,7 @@ fn check_pat_variant_nested_or_literal_with_depth( | ast::Pat::BoxPat(_) | ast::Pat::DerefPat(_) | ast::Pat::NotNull(_) + | ast::Pat::CfgPredPat(_) | ast::Pat::ConstBlockPat(_) => true, ast::Pat::IdentPat(ident_pat) => ident_pat.pat().is_some_and(|pat| { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index c2d7cb98cf9a8..792c1055f05eb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -74,6 +74,7 @@ pub(crate) fn complete_attribute_path( &AttrCtx { kind, annotated_item_kind, ref derive_helpers }: &AttrCtx, ) { let is_inner = kind == AttrKind::Inner; + let arg_exists = ctx.original_token.next_token().is_some_and(|it| it.kind().is_l_delimiter()); for (derive_helper, derive_name) in derive_helpers { let mut item = CompletionItem::new( @@ -113,7 +114,9 @@ pub(crate) fn complete_attribute_path( Qualified::No => { ctx.process_all_names(&mut |name, def, doc_aliases| match def { hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => { - acc.add_macro(ctx, path_ctx, m, name) + if !DUPLICATE_ATTR.contains(&name.as_str()) { + acc.add_macro(ctx, path_ctx, m, name) + } } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { acc.add_module(ctx, path_ctx, m, name, doc_aliases) @@ -147,6 +150,12 @@ pub(crate) fn complete_attribute_path( .zip(qualifiers) .take_while(|(s, q)| s.name_ref().is_some_and(|t| t.text() == **q)) .count(); + if let Some(s) = snippet.as_mut() + && (arg_exists && s.ends_with(')')) + && let Some(at) = s.find('(') + { + s.truncate(at); + } if matching_qualifiers != qualifiers.len() { let prefix = qualifiers[matching_qualifiers..].join("::"); label = format!("{prefix}::{label}"); @@ -390,6 +399,8 @@ const ATTRIBUTES: &[AttrCompletion] = &[ .prefer_inner(), ]; +const DUPLICATE_ATTR: &[&str] = &["derive", "test"]; + fn parse_comma_sep_expr(input: ast::TokenTree) -> Option> { let r_paren = input.r_paren_token()?; let tokens = input diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index 9c24ee1a5808b..8907fd589ac08 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -2,15 +2,25 @@ use ide_db::SymbolKind; use itertools::Itertools; -use syntax::{AstToken, Direction, NodeOrToken, SmolStr, SyntaxKind, algo, ast::Ident}; +use syntax::{ + AstToken, Direction, NodeOrToken, SmolStr, SyntaxKind, algo, + ast::{self, Ident, IsString}, +}; use crate::{CompletionItem, completions::Completions, context::CompletionContext}; pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_>) { + let (token_range, snip) = if let Some(string) = ast::String::cast(ctx.original_token.clone()) + && let Some(range) = string.text_range_between_quotes() + { + (range, str::to_owned as for<'a> fn(&'a str) -> _) + } else { + (ctx.source_range(), (|s| format!(r#""{s}""#)) as for<'a> fn(&'a str) -> _) + }; let add_completion = |item: &str| { let mut completion = - CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), item, ctx.edition); - completion.insert_text(format!(r#""{item}""#)); + CompletionItem::new(SymbolKind::BuiltinAttr, token_range, item, ctx.edition); + completion.insert_text(snip(item)); completion.add_to(acc, ctx.db); }; @@ -41,14 +51,9 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_ "target_endian" => ["little", "big"].into_iter().for_each(add_completion), name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).for_each(|s| { let s = s.as_str(); - let insert_text = format!(r#""{s}""#); - let mut item = CompletionItem::new( - SymbolKind::BuiltinAttr, - ctx.source_range(), - s, - ctx.edition, - ); - item.insert_text(insert_text); + let mut item = + CompletionItem::new(SymbolKind::BuiltinAttr, token_range, s, ctx.edition); + item.insert_text(snip(s)); item.add_to(acc, ctx.db); }), }, @@ -61,7 +66,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_ hir::CfgAtom::KeyValue { key, .. } => ( key.as_str(), if ctx.config.snippet_cap.is_some() { - SmolStr::from_iter([key.as_str(), " = $0"]) + SmolStr::from_iter([key.as_str(), " = \"$1\""]) } else { SmolStr::default() }, @@ -70,12 +75,8 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_ .chain(CFG_CONDITION.iter().map(|&(k, snip)| (k, SmolStr::new_static(snip)))) .unique_by(|&(s, _)| s) .for_each(|(s, snippet)| { - let mut item = CompletionItem::new( - SymbolKind::BuiltinAttr, - ctx.source_range(), - s, - ctx.edition, - ); + let mut item = + CompletionItem::new(SymbolKind::BuiltinAttr, token_range, s, ctx.edition); if let Some(cap) = ctx.config.snippet_cap && !snippet.is_empty() { @@ -86,8 +87,12 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_ } } -const CFG_CONDITION: &[(&str, &str)] = - &[("all", "all($0)"), ("any", "any($0)"), ("not", "not($0)")]; +const CFG_CONDITION: &[(&str, &str)] = &[ + ("all", "all($0)"), + ("any", "any($0)"), + ("not", "not($0)"), + ("false", "false"), // 'true' in 'potential_cfg' +]; const KNOWN_ARCH: [&str; 20] = [ "aarch64", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 3e95128bd70ef..d634bab72ca3f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -70,6 +70,7 @@ pub(crate) fn complete_expr_path<'db>( ref innermost_breakable_ty, ref impl_, in_match_guard, + at_optional_block, .. } = expr_ctx; @@ -383,9 +384,10 @@ pub(crate) fn complete_expr_path<'db>( if in_condition { add_keyword("letm", "let mut $1 = $0"); add_keyword("let", "let $1 = $0"); - } - - if in_block_expr { + } else if at_optional_block { + add_keyword("letm", "{\n let mut $1 = $0;\n}"); + add_keyword("let", "{\n let $1 = $0;\n}"); + } else if in_block_expr { add_keyword("letm", "let mut $1 = $0;"); add_keyword("let", "let $1 = $0;"); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index bf834e8b76b7e..bf4f219055101 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -33,6 +33,7 @@ use hir::{MacroCallId, Name}; use ide_db::text_edit::TextEdit; +use ide_db::traits::IsRequiredAssocItem; use ide_db::{ SymbolKind, documentation::HasDocs, path_transform::PathTransform, syntax_helpers::prettify_macro_expansion, traits::get_missing_assoc_items, @@ -165,17 +166,24 @@ fn complete_trait_impl( get_missing_assoc_items(&ctx.sema, impl_def) .into_iter() .filter(|(item, _)| ctx.check_stability_and_hidden(*item)) - .for_each(|(item, _)| { + .for_each(|(item, required)| { use self::ImplCompletionKind::*; match (item, kind) { (hir::AssocItem::Function(func), All | Fn) => { - add_function_impl(acc, ctx, replacement_range, func, hir_impl) + add_function_impl(acc, ctx, replacement_range, func, hir_impl, required) } (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => { - add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl) + add_type_alias_impl( + acc, + ctx, + replacement_range, + type_alias, + hir_impl, + required, + ) } (hir::AssocItem::Const(const_), All | Const) => { - add_const_impl(acc, ctx, replacement_range, const_, hir_impl) + add_const_impl(acc, ctx, replacement_range, const_, hir_impl, required) } _ => {} } @@ -189,6 +197,7 @@ fn add_function_impl( replacement_range: TextRange, func: hir::Function, impl_def: hir::Impl, + required: IsRequiredAssocItem, ) { let fn_name = &func.name(ctx.db); let sugar: &[_] = if func.is_async(ctx.db) { @@ -199,7 +208,16 @@ fn add_function_impl( &[AsyncSugaring::Plain] }; for &sugaring in sugar { - add_function_impl_(acc, ctx, replacement_range, func, impl_def, fn_name, sugaring); + add_function_impl_( + acc, + ctx, + replacement_range, + func, + impl_def, + fn_name, + sugaring, + required, + ); } } @@ -211,6 +229,7 @@ fn add_function_impl_( impl_def: hir::Impl, fn_name: &Name, async_sugaring: AsyncSugaring, + required: IsRequiredAssocItem, ) { let async_ = if let AsyncSugaring::Async | AsyncSugaring::Resugar = async_sugaring { "async " @@ -233,7 +252,11 @@ fn add_function_impl_( let mut item = CompletionItem::new(completion_kind, replacement_range, label, ctx.edition); item.lookup_by(format!("{}fn {}", async_, fn_name.display(ctx.db, ctx.edition))) .set_documentation(func.docs(ctx.db)) - .set_relevance(CompletionRelevance { exact_name_match: true, ..Default::default() }); + .set_relevance(CompletionRelevance { + exact_name_match: true, + is_missing: required.0, + ..Default::default() + }); if let Some(source) = ctx.sema.source(func) && let Some(transformed_fn) = @@ -383,6 +406,7 @@ fn add_type_alias_impl( replacement_range: TextRange, type_alias: hir::TypeAlias, impl_def: hir::Impl, + required: IsRequiredAssocItem, ) { let alias_name = type_alias.name(ctx.db).as_str().to_smolstr(); @@ -392,7 +416,11 @@ fn add_type_alias_impl( CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label, ctx.edition); item.lookup_by(format!("type {alias_name}")) .set_documentation(type_alias.docs(ctx.db)) - .set_relevance(CompletionRelevance { exact_name_match: true, ..Default::default() }); + .set_relevance(CompletionRelevance { + exact_name_match: true, + is_missing: required.0, + ..Default::default() + }); if let Some(source) = ctx.sema.source(type_alias) { let assoc_item = ast::AssocItem::TypeAlias(source.value); @@ -466,6 +494,7 @@ fn add_const_impl( replacement_range: TextRange, const_: hir::Const, impl_def: hir::Impl, + required: IsRequiredAssocItem, ) { let const_name = const_.name(ctx.db).map(|n| n.display_no_db(ctx.edition).to_smolstr()); @@ -490,6 +519,7 @@ fn add_const_impl( .set_documentation(const_.docs(ctx.db)) .set_relevance(CompletionRelevance { exact_name_match: true, + is_missing: required.0, ..Default::default() }); match ctx.config.snippet_cap { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index f7dd1589ae7d5..e1ad88dbd93d3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -841,6 +841,67 @@ fn main() { fn main() { if { let mut $1 = $0; } {} } +"#, + ); + } + + #[test] + fn completes_let_in_match_arm() { + check_edit( + "let", + r#" +fn main() { + match () { + () => $0 + } +} +"#, + r#" +fn main() { + match () { + () => { + let $1 = $0; +} + } +} +"#, + ); + check_edit( + "letm", + r#" +fn main() { + match () { + () => letm$0 + } +} +"#, + r#" +fn main() { + match () { + () => { + let mut $1 = $0; +} + } +} +"#, + ); + } + + #[test] + fn completes_let_in_closure_expr() { + check_edit( + "let", + r#" +fn main() { + || $0 +} +"#, + r#" +fn main() { + || { + let $1 = $0; +} +} "#, ); } 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 34b53e5e5bfea..832d752e7a1ca 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 @@ -17,7 +17,7 @@ use itertools::{Either, Itertools}; use stdx::never; use syntax::{ SmolStr, - SyntaxKind::{CLOSURE_EXPR, EXPR_STMT, MATCH_ARM, STMT_LIST}, + SyntaxKind::{CLOSURE_EXPR, MATCH_ARM}, T, TextRange, TextSize, ToSmolStr, ast::{self, AstNode, AstToken}, format_smolstr, match_ast, @@ -189,7 +189,7 @@ pub(crate) fn complete_postfix( postfix_snippet("let", "let", format!("let $1 = {receiver_text}")) .add_to(acc, ctx.db); } - _ if matches!(parent.kind(), STMT_LIST | EXPR_STMT) => { + _ if expr_ctx.in_block_expr => { postfix_snippet("let", "let", format!("let $0 = {receiver_text}{semi}")) .add_to(acc, ctx.db); postfix_snippet("letm", "let mut", format!("let mut $0 = {receiver_text}{semi}")) @@ -772,6 +772,38 @@ fn main() { sn while while expr {} "#]], ); + + check_edit( + "let", + r#" +fn foo() -> (i32, i32) { + "a".let$0 + (2, 3).clone() +} +"#, + r#" +fn foo() -> (i32, i32) { + let $0 = "a"; + (2, 3).clone() +} +"#, + ); + + check_edit( + "let", + r#" +fn foo() -> (i32, i32) { + "a".l$0 + (2, 3).clone() +} +"#, + r#" +fn foo() -> (i32, i32) { + let $0 = "a"; + (2, 3).clone() +} +"#, + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 0739084381b3b..f136f1f632b6e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -22,6 +22,7 @@ pub struct CompletionConfig<'a> { pub enable_auto_await: bool, pub enable_private_editable: bool, pub enable_term_search: bool, + pub enable_turbofish: bool, pub term_search_fuel: u64, pub full_function_signatures: bool, pub callable: Option, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 5935e16542e4a..099097c1750e2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -189,6 +189,7 @@ pub(crate) struct PathExprCtx<'db> { /// Whether this expression occurs in match arm guard position: before the /// fat arrow token pub(crate) in_match_guard: bool, + pub(crate) at_optional_block: bool, } /// Original file ast nodes @@ -492,6 +493,7 @@ pub(crate) struct CompletionContext<'a, 'db> { pub(crate) expected_name: Option, /// The expected type of what we are completing. pub(crate) expected_type: Option>, + pub(crate) expected_func: Option>, pub(crate) qualifier_ctx: QualifierCtx, @@ -520,6 +522,13 @@ pub(crate) struct CompletionContext<'a, 'db> { pub(crate) complete_semicolon: CompleteSemicolon, } +#[derive(Debug, Default)] +pub(crate) struct ExpectedInfo<'db> { + expected_type: Option>, + expected_name: Option, + expected_func: Option>, +} + #[derive(Debug)] pub(crate) enum CompleteSemicolon { DoNotComplete, @@ -794,7 +803,7 @@ impl<'a, 'db> CompletionContext<'a, 'db> { let AnalysisResult { analysis, - expected: (expected_type, expected_name), + expected: ExpectedInfo { expected_type, expected_name, expected_func }, qualifier_ctx, token, original_offset, @@ -935,6 +944,7 @@ impl<'a, 'db> CompletionContext<'a, 'db> { edition, expected_name, expected_type, + expected_func, qualifier_ctx, locals, depth_from_crate_root, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 7280fd1ad5751..e5ee0d5978dd1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -25,9 +25,9 @@ use crate::{ completions::postfix::{is_in_condition, is_in_value}, context::{ AttrCtx, BreakableKind, COMPLETION_MARKER, CompletionAnalysis, DotAccess, DotAccessExprCtx, - DotAccessKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, - NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathExprCtx, - PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx, + DotAccessKind, ExpectedInfo, ItemListKind, LifetimeContext, LifetimeKind, NameContext, + NameKind, NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, + PathExprCtx, PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx, TypeAscriptionTarget, TypeLocation, }, }; @@ -46,7 +46,7 @@ struct ExpansionResult { pub(super) struct AnalysisResult<'db> { pub(super) analysis: CompletionAnalysis<'db>, - pub(super) expected: (Option>, Option), + pub(super) expected: ExpectedInfo<'db>, pub(super) qualifier_ctx: QualifierCtx, /// the original token of the expanded file pub(super) token: SyntaxToken, @@ -444,8 +444,7 @@ fn analyze<'db>( expansion_result: ExpansionResult, original_token: &SyntaxToken, self_token: &SyntaxToken, -) -> Option<(CompletionAnalysis<'db>, (Option>, Option), QualifierCtx)> -{ +) -> Option<(CompletionAnalysis<'db>, ExpectedInfo<'db>, QualifierCtx)> { let _p = tracing::info_span!("CompletionContext::analyze").entered(); let ExpansionResult { original_file, @@ -487,7 +486,7 @@ fn analyze<'db>( } return Some(( CompletionAnalysis::NameRef(nameref_ctx), - (None, None), + ExpectedInfo::default(), QualifierCtx::default(), )); } @@ -495,7 +494,9 @@ fn analyze<'db>( } let Some(name_like) = find_node_at_offset(&speculative_file, speculative_offset) else { - let analysis = if let Some(original) = ast::String::cast(original_token.clone()) { + let analysis = if let Some(original) = ast::String::cast(original_token.clone()) + && original_token.parent().is_none_or(|it| !ast::CfgAtom::can_cast(it.kind())) + { CompletionAnalysis::String { original, expanded: ast::String::cast(self_token.clone()) } } else { // Fix up trailing whitespace problem @@ -541,7 +542,7 @@ fn analyze<'db>( return None; } }; - return Some((analysis, (None, None), QualifierCtx::default())); + return Some((analysis, ExpectedInfo::default(), QualifierCtx::default())); }; let expected = expected_type_and_name(sema, self_token, &name_like); @@ -590,11 +591,11 @@ fn expected_type_and_name<'db>( sema: &Semantics<'db, RootDatabase>, self_token: &SyntaxToken, name_like: &ast::NameLike, -) -> (Option>, Option) { +) -> ExpectedInfo<'db> { let token = prev_special_biased_token_at_trivia(self_token.clone()); let mut node = match token.parent() { Some(it) => it, - None => return (None, None), + None => return ExpectedInfo::default(), }; let strip_refs = |mut ty: Type<'db>| match name_like { @@ -649,6 +650,7 @@ fn expected_type_and_name<'db>( }; let mut generic_def = None; + let mut original_ty = None; let mut rebase_ty = { let node = node.clone(); move |ty: hir::Type<'db>| { @@ -709,6 +711,7 @@ fn expected_type_and_name<'db>( token.clone(), ).map(|ap| { let name = ap.ident().map(NameOrNameRef::Name); + original_ty.get_or_insert(ap.original.map(|it| it.ty().clone())); (Some(ap.ty), name) }) .unwrap_or((None, None)) @@ -876,7 +879,20 @@ fn expected_type_and_name<'db>( } }; }; - (ty.map(strip_refs), name) + // FIXME: Heuristic hack for PathExpr in ArgList + let expected_func = original_ty.flatten().and_then(|ty| { + let is_direct_arg = token + .parent() + .filter(|it| ast::NameLike::can_cast(it.kind()) || token.kind() == T![::]) + .into_iter() + .flat_map(|it| it.ancestors().skip(1)) + .find(|it| { + !Either::, ast::PathExpr>::can_cast(it.kind()) + }) + == Some(node); + is_direct_arg.then_some(()).and_then(|()| ty.as_callable(sema.db)) + }); + ExpectedInfo { expected_type: ty.map(strip_refs), expected_name: name, expected_func } } fn classify_lifetime( @@ -1032,7 +1048,7 @@ fn classify_name_ref<'db>( match parent { ast::PathSegment(segment) => segment, ast::FieldExpr(field) => { - return field_expr_handle(field.expr(), field.syntax()); + return field_expr_handle(field.expr(), name_ref.syntax()); }, ast::ExternCrate(_) => { let kind = NameRefKind::ExternCrate; @@ -1041,14 +1057,14 @@ fn classify_name_ref<'db>( ast::MethodCallExpr(method) => { let receiver = find_opt_node_in_file(original_file, method.receiver()); let has_parens = has_parens(&method); - if !has_parens && let Some(res) = field_expr_handle(method.receiver(), method.syntax()) { + if !has_parens && let Some(res) = field_expr_handle(method.receiver(), name_ref.syntax()) { return Some(res) } let kind = NameRefKind::DotAccess(DotAccess { receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)), kind: DotAccessKind::Method, receiver, - ctx: DotAccessExprCtx { in_block_expr: is_in_block(method.syntax()), in_breakable: is_in_breakable(method.syntax()).unzip().0 } + ctx: DotAccessExprCtx { in_block_expr: is_in_block(name_ref.syntax()), in_breakable: is_in_breakable(name_ref.syntax()).unzip().0 } }); return Some(make_res(kind)); }, @@ -1424,12 +1440,13 @@ fn classify_name_ref<'db>( let impl_ = fetch_immediate_impl_or_trait(sema, original_file, expr.syntax()) .and_then(Either::left); - let in_match_guard = match it.parent().and_then(ast::MatchArm::cast) { - Some(arm) => arm - .fat_arrow_token() - .is_none_or(|arrow| it.text_range().start() < arrow.text_range().start()), - None => false, - }; + let before_match_arrow = it.parent().and_then(ast::MatchArm::cast).map(|arm| { + arm.fat_arrow_token() + .is_none_or(|arrow| it.text_range().start() < arrow.text_range().start()) + }); + let in_match_guard = before_match_arrow == Some(true); + let at_optional_block = before_match_arrow == Some(false) + || it.parent().and_then(ast::ClosureExpr::cast).is_some(); PathKind::Expr { expr_ctx: PathExprCtx { @@ -1449,6 +1466,7 @@ fn classify_name_ref<'db>( after_incomplete_let, impl_, in_match_guard, + at_optional_block, }, } }; @@ -1725,12 +1743,14 @@ fn classify_name_ref<'db>( qualifier_ctx.abi_node = error_node.children().find_map(ast::Abi::cast); } + let allowed_prev_kinds = + [T![;], T!['}'], T!['{'], T![']'], SyntaxKind::INNER_DOC_COMMENT]; if let PathKind::Item { .. } = path_ctx.kind && qualifier_ctx.none() && let Some(t) = top.first_token() && let Some(prev) = t.prev_token().and_then(|t| syntax::algo::skip_trivia_token(t, Direction::Prev)) - && ![T![;], T!['}'], T!['{'], T![']']].contains(&prev.kind()) + && !allowed_prev_kinds.contains(&prev.kind()) { // This was inferred to be an item position path, but it seems // to be part of some other broken node which leaked into an item @@ -2024,11 +2044,20 @@ fn is_in_breakable(node: &SyntaxNode) -> Option<(BreakableKind, SyntaxNode)> { } fn is_in_block(node: &SyntaxNode) -> bool { - if has_in_newline_expr_first(node) { + let in_rightside = ast::NameRef::can_cast(node.kind()); + let sense_node = node.ancestors().nth(in_rightside.into()).unwrap_or_else(|| node.clone()); + if has_in_newline_expr_first(node, &sense_node) { return true; }; - node.parent() - .map(|node| ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind())) + sense_node + .parent() + .map(|node| { + ast::ExprStmt::can_cast(node.kind()) + || ast::StmtList::can_cast(node.kind()) + // Because `is_in_block` is only called in the context of an expression, + // there is no need to consider expanding it into a type or pattern + || ast::MacroStmts::can_cast(node.kind()) + }) .unwrap_or(false) } @@ -2036,16 +2065,21 @@ fn is_in_block(node: &SyntaxNode) -> bool { /// /// Heuristic: /// -/// If the `PathExpr` is left part of the `Expr` and there is a newline after the `PathExpr`, -/// it is considered that the `PathExpr` is not part of the `Expr`. -fn has_in_newline_expr_first(node: &SyntaxNode) -> bool { - if ast::PathExpr::can_cast(node.kind()) - && let Some(NodeOrToken::Token(next)) = node.next_sibling_or_token() +/// Check if `node` follows a newline and if the ancestors of `sense_node` can be split all the way to `StmtList` +fn has_in_newline_expr_first(node: &SyntaxNode, sense_node: &SyntaxNode) -> bool { + let sense_mode = sense_node != node; + let is_splitable = |node: &SyntaxNode| { + let range = node.text_range(); + let sense = sense_node.text_range(); + range.start() == sense.start() || sense_mode && range.end() == sense.end() + }; + if (ast::PathExpr::can_cast(node.kind()) || sense_mode) + && let Some(next) = node.last_token().and_then(|it| it.next_token()) && next.kind() == SyntaxKind::WHITESPACE && next.text().contains('\n') - && let Some(stmt_like) = node + && let Some(stmt_like) = sense_node .ancestors() - .take_while(|it| it.text_range().start() == node.text_range().start()) + .take_while(is_splitable) .filter_map(Either::::cast) .last() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index f5c54832a12ce..95c966d609818 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -516,13 +516,12 @@ fn render_resolution_path<'db>( ScopeDef::ModuleDef(ModuleDef::Const(konst)) => set_item_relevance(konst.ty(db)), ScopeDef::ModuleDef(ModuleDef::Static(stat)) => set_item_relevance(stat.ty(db)), ScopeDef::ModuleDef(ModuleDef::BuiltinType(bt)) => set_item_relevance(bt.ty(db)), + ScopeDef::ModuleDef(ModuleDef::TypeAlias(ta)) => set_item_relevance(ta.ty(db)), ScopeDef::ImplSelfType(imp) => set_item_relevance(imp.self_ty(db)), ScopeDef::GenericParam(_) | ScopeDef::Label(_) | ScopeDef::Unknown - | ScopeDef::ModuleDef( - ModuleDef::Trait(_) | ModuleDef::Module(_) | ModuleDef::TypeAlias(_), - ) => (), + | ScopeDef::ModuleDef(ModuleDef::Trait(_) | ModuleDef::Module(_)) => (), }; item @@ -689,7 +688,10 @@ fn compute_type_match( // We don't ever consider unit type to be an exact type match, since // nearly always this is not meaningful to the user. - if expected_type.is_unit() { + if expected_type.is_unit() || completion_ty.is_unit() { + return None; + } + if expected_type.is_unknown() || completion_ty.is_unknown() { return None; } @@ -717,7 +719,11 @@ fn compute_has_local_inherent_impl( } fn compute_exact_name_match(ctx: &CompletionContext<'_, '_>, completion_name: &str) -> bool { - ctx.expected_name.as_ref().is_some_and(|name| name.text() == completion_name) + ctx.expected_name.as_ref().is_some_and(|name| { + let name = name.text().strip_prefix("r#").unwrap_or(name.text()); + let completion_name = completion_name.strip_prefix("r#").unwrap_or(completion_name); + name.trim_matches('_') == completion_name.trim_matches('_') + }) } fn compute_ref_match( @@ -1245,6 +1251,35 @@ fn func(input: Struct) { } ); } + #[test] + fn set_type_alias_completion_info() { + check_relevance( + r#" +//- /main.rs crate:main +struct Struct; +type Alias = Struct; + +impl Struct { + fn test(&self) { + func(Self$0); + } +} +fn func(input: Struct) { } +"#, + expect![[r#" + st Self Self [type] + st Struct Struct [type] + ta Alias Struct [type] + sp Self Struct [type] + st Struct Struct [type] + ex Struct [type] + lc self &Struct [local] + fn func(…) fn(Struct) [] + me self.test() fn(&self) [] + "#]], + ); + } + #[test] fn set_builtin_type_completion_info() { check_relevance( @@ -2076,13 +2111,16 @@ fn main() { A$0 } CompletionItem { label: "A", detail_left: None, - detail_right: None, + detail_right: Some( + "i32", + ), source_range: 41..42, delete: 41..42, insert: "A", kind: SymbolKind( TypeAlias, ), + detail: "i32", deprecated: true, relevance: CompletionRelevance { exact_name_match: false, @@ -2174,9 +2212,7 @@ fn main() { A { the$0 } } deprecated: true, relevance: CompletionRelevance { exact_name_match: false, - type_match: Some( - CouldUnify, - ), + type_match: None, is_local: false, is_missing: false, trait_: None, @@ -2465,6 +2501,229 @@ fn main() -> RawIdentTable { ); } + #[test] + fn no_call_parens_if_param_with_fn_bounds() { + cov_mark::check!(no_call_parens_if_ty_with_fn_bounds); + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(f$0) +} +"#, + r#" +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(foo) +} +"#, + ); + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: F) {} +fn foo(a: u8, b: u8) { + needs_fn(f$0) +} +"#, + r#" +fn needs_fn(f: F) {} +fn foo(a: u8, b: u8) { + needs_fn(foo) +} +"#, + ); + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: F) where F: FnOnce(u8, u8) {} +fn foo(a: u8, b: u8) { + needs_fn(f$0) +} +"#, + r#" +fn needs_fn(f: F) where F: FnOnce(u8, u8) {} +fn foo(a: u8, b: u8) { + needs_fn(foo) +} +"#, + ); + check_edit( + "unmap", + r#" +//- minicore: fn +trait Trait { + fn unmap(self) -> i32 { 0 } +} +mod module { + pub struct Ty; + impl super::Trait for Ty {} +} +fn needs_fn(f: impl FnOnce(T) -> i32) {} +fn foo(a: u8, b: u8) { + needs_fn(module::Ty::u$0) +} +"#, + r#" +trait Trait { + fn unmap(self) -> i32 { 0 } +} +mod module { + pub struct Ty; + impl super::Trait for Ty {} +} +fn needs_fn(f: impl FnOnce(T) -> i32) {} +fn foo(a: u8, b: u8) { + needs_fn(module::Ty::unmap) +} +"#, + ); + check_edit( + "neg", + r#" +//- minicore: iterator, iterators, unary_ops, builtin_impls +fn foo(iter: impl Iterator) { + iter.map(core::ops::Neg::n$0) +} +"#, + r#" +fn foo(iter: impl Iterator) { + iter.map(core::ops::Neg::neg) +} +"#, + ); + check_edit( + "neg", + r#" +//- minicore: iterator, iterators, unary_ops, builtin_impls +fn foo(iter: impl Iterator) { + iter.map(core::ops::Neg::$0) +} +"#, + r#" +fn foo(iter: impl Iterator) { + iter.map(core::ops::Neg::neg) +} +"#, + ); + } + + #[test] + fn call_parens_in_non_direct_expr() { + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(&f$0) +} +"#, + r#" +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(&foo(${1:a}, ${2:b})$0) +} +"#, + ); + check_edit( + "method", + r#" +//- minicore: fn +struct Foo; +impl Foo { fn method(&self, a: u8, b: u8) {} } +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(Foo.m$0) +} +"#, + r#" +struct Foo; +impl Foo { fn method(&self, a: u8, b: u8) {} } +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(Foo.method(${1:a}, ${2:b})$0) +} +"#, + ); + check_edit( + "method", + r#" +//- minicore: fn +struct Foo; +impl Foo { fn method(&self, a: u8, b: u8) {} } +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(Foo.$0) +} +"#, + r#" +struct Foo; +impl Foo { fn method(&self, a: u8, b: u8) {} } +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8, b: u8) { + needs_fn(Foo.method(${1:a}, ${2:b})$0) +} +"#, + ); + } + + #[test] + fn call_parens_in_bad_case() { + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8) { + needs_fn(f$0); +} +"#, + r#" +fn needs_fn(f: impl FnOnce(u8, u8)) {} +fn foo(a: u8) { + needs_fn(foo(${1:a})$0); +} +"#, + ); + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: impl FnOnce(u8, u8) -> T) {} +fn foo(a: u8, b: u8) -> impl FnOnce(u8, u8) { + needs_fn(f$0); +} +"#, + r#" +fn needs_fn(f: impl FnOnce(u8, u8) -> T) {} +fn foo(a: u8, b: u8) -> impl FnOnce(u8, u8) { + needs_fn(foo(${1:a}, ${2:b})$0); +} +"#, + ); + check_edit( + "foo", + r#" +//- minicore: fn +fn needs_fn(f: impl FnOnce(u8, u8) -> u8) {} +fn foo(a: u8, b: u8) -> u16 { + needs_fn(f$0); +} +"#, + r#" +fn needs_fn(f: impl FnOnce(u8, u8) -> u8) {} +fn foo(a: u8, b: u8) -> u16 { + needs_fn(foo(${1:a}, ${2:b})$0); +} +"#, + ); + } + #[test] fn no_parens_in_use_item() { check_edit( @@ -3023,6 +3282,52 @@ fn f() { ); } + #[test] + fn name_match_special_case() { + check_relevance( + r#" +fn foo(_db: &()) {} +fn bar(db: &()) { + foo($0) +} +"#, + expect![[r#" + lc db &() [type+name+local] + ex db [type] + fn bar(…) fn(&()) [] + fn foo(…) fn(&()) [] + "#]], + ); + check_relevance( + r#" +fn foo(impl_: &()) {} +fn bar(r#impl: &()) { + foo($0) +} +"#, + expect![[r#" + lc impl &() [type+name+local] + ex r#impl [type] + fn bar(…) fn(&()) [] + fn foo(…) fn(&()) [] + "#]], + ); + check_relevance( + r#" +fn foo(r#impl: &()) {} +fn bar(impl_: &()) { + foo($0) +} +"#, + expect![[r#" + lc impl_ &() [type+name+local] + ex impl_ [type] + fn bar(…) fn(&()) [] + fn foo(…) fn(&()) [] + "#]], + ); + } + #[test] fn score_has_local_inherent_impl() { check_relevance( @@ -3096,6 +3401,66 @@ fn go(foo: Foo) { match foo { Foo($0) } } ); } + #[test] + fn not_type_match_useless_types() { + check_relevance( + r#" +fn foo() -> Unknown {} +fn test() { + let unit = (); + let unknown = unknown; + let _: () = $0; +} +"#, + expect![[r#" + lc unit () [local] + fn foo() fn() -> {unknown} [] + fn test() fn() [] + "#]], + ); + check_relevance( + r#" +fn foo() -> Unknown {} +fn test() { + let unit = (); + let unknown = unknown; + let _ = $0; +} +"#, + expect![[r#" + lc unit () [local] + fn foo() fn() -> {unknown} [] + fn test() fn() [] + "#]], + ); + check_relevance( + r#" +struct Matches; +impl Matches { + fn opt_present(&self) -> bool {} +} +mod module { + pub fn max(a: T, b: T) -> T {} + pub fn min(a: T, b: T) -> T {} +} +use module::max; +fn test(matches: &Matches) { + if m$0 +} +"#, + expect![[r#" + ex false [type] + ex true [type] + lc matches &Matches [local] + st Matches Matches [] + fn max(…) fn(T, T) -> T [] + md module:: [] + fn test(…) fn(&Matches) [] + fn min(…) fn(T, T) -> T [requires_import] + "#]], + ); + } + #[test] fn test_avoid_redundant_suggestion() { check_relevance( @@ -3539,9 +3904,59 @@ fn test() { } "#, expect![[r#" - fn fn_ctr() fn() -> Foo [type_could_unify] - fn fn_ctr_self() fn() -> Option [type_could_unify] - fn fn_another(…) fn(u32) -> Other [type_could_unify] + fn fn_ctr() fn() -> Foo [] + fn fn_ctr_self() fn() -> Option [] + fn fn_another(…) fn(u32) -> Other [] + "#]], + ); + } + + #[test] + fn trait_items_order() { + check_relevance( + r#" +trait T{ + fn a_optional() {} + fn b_required(); +} + +impl T for (){ + $0 +} +"#, + expect![[r#" + fn fn b_required() [name+missing] + fn fn a_optional() [name] + "#]], + ); + check_relevance( + r#" +trait T{ + type Item; + fn optional() {} +} +impl T for (){ + $0 +} +"#, + expect![[r#" + ta type Item = [name+missing] + fn fn optional() [name] + "#]], + ); + check_relevance( + r#" +trait T{ + const A: usize = 0; + const B: usize; +} +impl T for (){ + $0 +} +"#, + expect![[r#" + ct const B: usize = [name+missing] + ct const A: usize = [name] "#]], ); } @@ -3685,12 +4100,12 @@ fn test() { // Constructor // Others expect![[r#" - fn fn_direct_ctr() fn() -> Foo [type_could_unify] - fn fn_ctr_with_args(…) fn(u32) -> Foo [type_could_unify] - fn fn_builder() fn() -> FooBuilder [type_could_unify] - fn fn_ctr() fn() -> Result [type_could_unify] - me fn_no_ret(…) fn(&self) [type_could_unify] - fn fn_other() fn() -> Result [type_could_unify] + fn fn_direct_ctr() fn() -> Foo [] + fn fn_ctr_with_args(…) fn(u32) -> Foo [] + fn fn_builder() fn() -> FooBuilder [] + fn fn_ctr() fn() -> Result [] + me fn_no_ret(…) fn(&self) [] + fn fn_other() fn() -> Result [] "#]], ); @@ -3721,13 +4136,13 @@ fn test() { } "#, expect![[r#" - fn fn_direct_ctr() fn() -> Foo [type_could_unify] - fn fn_ctr_with_args(…) fn(T) -> Foo [type_could_unify] - fn fn_builder() fn() -> FooBuilder [type_could_unify] - fn fn_ctr_wrapped() fn() -> Option> [type_could_unify] - fn fn_ctr_wrapped_2() fn() -> Result, u32> [type_could_unify] - fn fn_other() fn() -> Option [type_could_unify] - me fn_returns_unit(…) fn(&self) [type_could_unify] + fn fn_direct_ctr() fn() -> Foo [] + fn fn_ctr_with_args(…) fn(T) -> Foo [] + fn fn_builder() fn() -> FooBuilder [] + fn fn_ctr_wrapped() fn() -> Option> [] + fn fn_ctr_wrapped_2() fn() -> Result, u32> [] + fn fn_other() fn() -> Option [] + me fn_returns_unit(…) fn(&self) [] "#]], ); } @@ -3757,13 +4172,13 @@ fn test() { } "#, expect![[r#" - fn fn_direct_ctr() fn() -> Foo [type_could_unify] - fn fn_ctr_with_args(…) fn(T) -> Foo [type_could_unify] - fn fn_builder() fn() -> FooBuilder [type_could_unify] - fn fn_ctr() fn() -> Option> [type_could_unify] - fn fn_ctr2() fn() -> Result, u32> [type_could_unify] - me fn_no_ret(…) fn(&self) [type_could_unify] - fn fn_other() fn() -> Option [type_could_unify] + fn fn_direct_ctr() fn() -> Foo [] + fn fn_ctr_with_args(…) fn(T) -> Foo [] + fn fn_builder() fn() -> FooBuilder [] + fn fn_ctr() fn() -> Option> [] + fn fn_ctr2() fn() -> Result, u32> [] + me fn_no_ret(…) fn(&self) [] + fn fn_other() fn() -> Option [] "#]], ); } @@ -4175,8 +4590,22 @@ fn main() { expect![[r#" ct INFINITY pub const INFINITY: f32 [] ct NEG_INFINITY pub const NEG_INFINITY: f32 [] - ct INFINITY f32 [type_could_unify+requires_import+deprecated] - ct NEG_INFINITY f32 [type_could_unify+requires_import+deprecated] + ct INFINITY f32 [requires_import+deprecated] + ct NEG_INFINITY f32 [requires_import+deprecated] + "#]], + ); + check_relevance( + r#" +//- minicore: float_consts +fn main() { + let x: f32 = f32::INF$0 +} +"#, + expect![[r#" + ct INFINITY pub const INFINITY: f32 [type] + ct NEG_INFINITY pub const NEG_INFINITY: f32 [type] + ct INFINITY f32 [type+requires_import+deprecated] + ct NEG_INFINITY f32 [type+requires_import+deprecated] "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs index 4922c72468ae1..4ae4dca3a1129 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs @@ -27,7 +27,13 @@ fn render(ctx: RenderContext<'_, '_>, const_: hir::Const) -> Option (false, false, None), }; - let complete_call_parens = cap - .filter(|_| !has_call_parens) - .and_then(|cap| Some((cap, params(ctx.completion, func, &func_kind, has_dot_receiver)?))); + let complete_call_parens = cap.filter(|_| !has_call_parens).and_then(|cap| { + Some((cap, params(ctx.completion, func, &func_kind, has_dot_receiver, &ret_type)?)) + }); let function = assoc_item .and_then(|assoc_item| assoc_item.implementing_ty(db)) @@ -162,6 +162,7 @@ fn render( self_param, params, &ret_type, + func, ); } @@ -218,11 +219,23 @@ pub(super) fn add_call_parens<'b>( self_param: Option, params: Vec>, ret_type: &hir::Type<'_>, + func: hir::Function, ) -> &'b mut Builder { cov_mark::hit!(inserts_parens_for_function_calls); let (mut snippet, label_suffix) = if self_param.is_none() && params.is_empty() { - (format!("{escaped_name}()$0"), "()") + let snippet = if ctx.config.enable_turbofish + && let gen_params = hir::GenericDef::Function(func).type_or_const_params(ctx.db) + && gen_params.iter().any(|it| it.as_type_param(ctx.db).is_some()) + && !ret_type.contains_unknown() + { + // Return type not contained any generic param, so can't infer param + // `fn size_of() -> usize` -> `size_of::<$1>()$0` + format!("{escaped_name}::<$1>()$0") + } else { + format!("{escaped_name}()$0") + }; + (snippet, "()") } else { builder.trigger_call_info(); let snippet = if let Some(CallableSnippets::FillArguments) = ctx.config.callable { @@ -376,6 +389,7 @@ fn params<'db>( func: hir::Function, func_kind: &FuncKind<'_>, has_dot_receiver: bool, + ret_type: &hir::Type<'_>, ) -> Option<(Option, Vec>)> { ctx.config.callable.as_ref()?; @@ -388,6 +402,17 @@ fn params<'db>( cov_mark::hit!(no_call_parens_if_fn_ptr_needed); return None; } + // Handle generic signature, do not use FnSig::eq + if ctx.expected_type.as_ref().is_none_or(|it| it.is_unknown()) // only process generic expected + && let Some(expected) = &ctx.expected_func + && let Some(completed) = func.ty(ctx.db).as_callable(ctx.db) + && expected.n_params() == completed.n_params() + && ctx.rebase_ty(&expected.return_type()).could_unify_with(ctx.db, ret_type) + && !completed.return_type().impls_fnonce(ctx.db) + { + cov_mark::hit!(no_call_parens_if_ty_with_fn_bounds); + return None; + } let self_param = if has_dot_receiver || matches!(func_kind, FuncKind::Method(_, Some(_))) { None @@ -990,6 +1015,120 @@ fn foo() {} fn bar() { match foo()$0 {} } +"#, + ); + } + + #[test] + fn turbofish() { + check_edit( + "size_of", + r#" +//- minicore: size_of +fn main() { + si$0 +} +"#, + r#" +fn main() { + size_of::<$1>()$0 +} +"#, + ); + check_edit_with_config( + CompletionConfig { enable_turbofish: false, ..TEST_CONFIG }, + "size_of", + r#" +//- minicore: size_of +fn main() { + si$0 +} +"#, + r#" +fn main() { + size_of()$0 +} +"#, + ); + check_edit( + "new", + r#" +struct Layout; +impl Layout { + pub const fn new() -> Self { + Self + } +} +fn main() { + Layout::$0 +} +"#, + r#" +struct Layout; +impl Layout { + pub const fn new() -> Self { + Self + } +} +fn main() { + Layout::new::<$1>()$0 +} +"#, + ); + check_edit( + "handle", + r#" +//- minicore: result +struct State; +enum E { E } +impl State { + fn handle(&mut self) -> Result<(), E> { + } +} +fn main() { + State.$0 +} +"#, + r#" +struct State; +enum E { E } +impl State { + fn handle(&mut self) -> Result<(), E> { + } +} +fn main() { + State.handle::<$1>()$0 +} +"#, + ); + } + + #[test] + fn no_turbofish() { + check_edit( + "handle", + r#" +//- minicore: result +struct State; +enum E { E } +impl State { + fn handle(&mut self) -> Result { + } +} +fn main() { + State.$0 +} +"#, + r#" +struct State; +enum E { E } +impl State { + fn handle(&mut self) -> Result { + } +} +fn main() { + State.handle()$0 +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs index ce1be2d55117d..870d06b122cc0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs @@ -49,7 +49,13 @@ fn render( item.set_documentation(ctx.docs(type_alias)) .set_deprecated(ctx.is_deprecated(type_alias, type_alias.as_assoc_item(db))) .detail(detail) - .set_relevance(ctx.completion_relevance()); + .set_relevance(crate::CompletionRelevance { + type_match: super::compute_type_match( + ctx.completion, + &ctx.completion.rebase_ty(&type_alias.ty(db)), + ), + ..ctx.completion_relevance() + }); if let Some(actm) = type_alias.as_assoc_item(db) && let Some(trt) = actm.container_or_implemented_trait(db) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index e574d4de0ac75..27af306b5f27f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -69,6 +69,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig { enable_self_on_the_fly: true, enable_private_editable: false, enable_term_search: true, + enable_turbofish: true, term_search_fuel: 200, full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), @@ -278,7 +279,21 @@ pub(crate) fn check_with_base_items( #[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect, ) { - check(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"), expect) + fn concat_fixture(prefix: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) -> String { + let mut buf = String::with_capacity(prefix.len() + ra_fixture.len()); + for line in ra_fixture.split_inclusive('\n') { + if line.trim().is_empty() || line.starts_with("//-") { + buf.push_str(line); + continue; + } + let rest = &ra_fixture[buf.len()..]; + buf.push_str(prefix); + buf.push_str(rest); + break; + } + buf + } + check(&concat_fixture(BASE_ITEMS_FIXTURE, ra_fixture), expect) } pub(crate) fn check_no_kw(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index bcc103b89fa11..5fde50969bcd4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -28,7 +28,6 @@ pub struct Foo(#[m$0] i32); at cold at deny(…) at deprecated - at derive macro derive at derive(…) at diagnostic::do_not_recommend at diagnostic::on_unimplemented @@ -92,7 +91,6 @@ pub struct Foo(#[$0] i32); at cold at deny(…) at deprecated - at derive macro derive at derive(…) at diagnostic::do_not_recommend at diagnostic::on_unimplemented @@ -140,6 +138,7 @@ fn proc_macros() { check( r#" //- proc_macros: identity +//- minicore: derive #[$0] struct Foo; "#, @@ -150,6 +149,7 @@ struct Foo; at deny(…) at deprecated at derive(…) + at derive_const macro derive_const at doc = "…" at doc = include_str!("…") at doc(alias = "…") @@ -162,6 +162,7 @@ struct Foo; at repr(…) at unsafe(…) at warn(…) + md core:: md proc_macros:: kw crate:: kw self:: @@ -448,7 +449,6 @@ struct Foo; at cfg_attr(…) at deny(…) at deprecated - at derive macro derive at derive(…) at derive_const macro derive_const at doc = "…" @@ -923,6 +923,12 @@ struct Foo; ); } +#[test] +fn attr_before_arg() { + check_edit("allow", r#"#[$0()] fn main() {}"#, r#"#[allow()] fn main() {}"#); + check_edit("cfg_attr", r#"#[c$0(test)] fn main() {}"#, r#"#[cfg_attr(test)] fn main() {}"#); +} + #[test] fn issue_17479() { check( @@ -959,6 +965,7 @@ mod cfg { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -974,6 +981,7 @@ mod cfg { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -993,6 +1001,7 @@ mod cfg { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -1008,6 +1017,7 @@ mod cfg { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -1023,6 +1033,7 @@ mod cfg { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -1038,6 +1049,7 @@ mod cfg { ba all ba any ba dbg + ba false ba not ba opt_level ba test @@ -1058,6 +1070,7 @@ fn f() {} expect![[r#" ba all ba any + ba false ba feature ba not ba true @@ -1088,7 +1101,7 @@ fn f() {} #[cfg($0)] "#, r#" -#[cfg(opt_level = $0)] +#[cfg(opt_level = "$1")] "#, ); } @@ -1109,6 +1122,16 @@ fn f() {} ba little "#]], ); + check( + r#"#[cfg(target_endian = "$0""#, + expect![[r#" + ba big + ba little + "#]], + ); + + check_edit("big", r#"#[cfg(target_endian = b$0"#, r#"#[cfg(target_endian = "big""#); + check_edit("big", r#"#[cfg(target_endian = "b$0""#, r#"#[cfg(target_endian = "big""#); } #[test] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 6e90bcd49bee4..49baf27961c2d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -344,6 +344,8 @@ fn complete_in_match_arm() { kw for kw if kw if let + kw let + kw letm kw loop kw match kw return @@ -807,7 +809,7 @@ fn foo() { fn in_macro_expr_frag() { check( r#" -macro_rules! m { ($e:expr) => { $e } } +macro_rules! m { ($e:expr) => { let _ = $e; } } fn quux(x: i32) { m!($0); } @@ -835,7 +837,7 @@ fn quux(x: i32) { ); check( r" -macro_rules! m { ($e:expr) => { $e } } +macro_rules! m { ($e:expr) => { let _ = $e; } } fn quux(x: i32) { m!(x$0); } @@ -859,11 +861,12 @@ fn quux(x: i32) { kw unsafe kw while kw while let + ex x "#]], ); check( r#" -macro_rules! m { ($e:expr) => { $e } } +macro_rules! m { ($e:expr) => { let _ = $e; } } fn quux(x: i32) { let y = 92; m!(x$0 @@ -893,6 +896,56 @@ fn quux(x: i32) { ); } +#[test] +fn in_identity_macro() { + check( + r#" +macro_rules! m { ($($t:tt)*) => { $($t)* } } +fn quux(x: i32) { + m!(l$0); +} +"#, + expect![[r#" + fn quux(…) fn(i32) + lc x i32 + ma m!(…) macro_rules! m + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + #[test] fn enum_qualified() { check_with_base_items( @@ -3289,7 +3342,7 @@ fn bar() { tt FromIterator tt IntoIterator tt Iterator - ta Result (use core::fmt::Result) + ta Result (use core::fmt::Result) Result<(), Error> ev Err(…) Err(E) ev None None ev Ok(…) Ok(T) @@ -3948,6 +4001,64 @@ fn fn_field_in_previous_line_of_ambiguous_expr() { ); } +#[test] +fn let_not_in_non_in_block_like_ambiguous_expr() { + // For cases where postfix completion is good, path completion is not good + check( + r#" + fn f() { + &l$0 + (1, 2).foo(); + }"#, + expect![[r#" + fn f() fn() + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw mut + kw raw + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); + check( + r#" + fn f() { + &l$0 + }"#, + expect![[r#" + fn f() fn() + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw mut + kw raw + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); +} + #[test] fn private_inherent_and_public_trait() { check( @@ -4094,20 +4205,23 @@ fn main() { ma bar macro_rules! bar ma foo macro_rules! foo bt u32 u32 - kw const kw crate:: kw false kw for kw if kw if let + kw let + kw letm kw loop kw match kw return kw self:: kw true - kw unsafe kw while kw while let + sn macro_rules + sn pd + sn ppd "#]], ); } @@ -4209,6 +4323,88 @@ fn foo(t: T) { ); } +#[test] +fn complete_cfg_macro() { + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn main() { + if cfg!(a$0) +} + "#, + expect![[r#" + ba all + ba any + ba dbg + ba false + ba not + ba opt_level + ba test + ba true + "#]], + ); + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn main() { + if cfg!($0) +} + "#, + expect![[r#" + ba all + ba any + ba dbg + ba false + ba not + ba opt_level + ba test + ba true + "#]], + ); + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn main() { + if cfg!(all($0)) +} + "#, + expect![[r#" + ba all + ba any + ba dbg + ba false + ba not + ba opt_level + ba test + ba true + "#]], + ); + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn main() { + match true { + cfg!(all($0)) => (), + } +} + "#, + expect![[r#" + ba all + ba any + ba dbg + ba false + ba not + ba opt_level + ba test + ba true + "#]], + ); +} + #[test] fn const_is_type_owner() { check( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs index 430c61887a20a..f9c3c8c01f0c2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs @@ -136,7 +136,38 @@ fn in_item_list_after_inner_attr() { sn tfn (Test function) sn tmod (Test module) "#]], - ) + ); + check_with_base_items( + r#"//! doc + $0"#, + expect![[r#" + ma makro!(…) macro_rules! makro + md module:: + kw async + kw const + kw crate:: + kw enum + kw extern + kw fn + kw impl + kw impl for + kw mod + kw pub + kw pub(crate) + kw pub(super) + kw self:: + kw static + kw struct + kw trait + kw type + kw union + kw unsafe + kw use + sn macro_rules + sn tfn (Test function) + sn tmod (Test module) + "#]], + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs index 506645261b8d5..4c373697d46a5 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs @@ -16,6 +16,7 @@ use crate::RootDatabase; pub struct ActiveParameter<'db> { pub ty: Type<'db>, pub src: Option>>, + pub original: Option>, } impl<'db> ActiveParameter<'db> { @@ -46,8 +47,13 @@ impl<'db> ActiveParameter<'db> { cov_mark::hit!(too_many_arguments); return None; } + let original = signature.as_function().map(|func| { + let mut assoc_params = func.assoc_fn_params(sema.db); + let assoc_idx = assoc_params.len() - params.len() + idx; + assoc_params.swap_remove(assoc_idx) + }); let param = params.swap_remove(idx); - Some(ActiveParameter { ty: param.ty().clone(), src: sema.source(param) }) + Some(ActiveParameter { ty: param.ty().clone(), src: sema.source(param), original }) } pub fn ident(&self) -> Option { 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 15ecabc33c81a..aaea0ca9a18a2 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 @@ -365,6 +365,7 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) { | ast::Expr::OffsetOfExpr(_) | ast::Expr::FormatArgsExpr(_) | ast::Expr::AsmExpr(_) + | ast::Expr::CfgPredExpr(_) | ast::Expr::IncludeBytesExpr(_) => 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 42bfc7fe4b21a..7c25a4c868fbe 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 @@ -186,16 +186,13 @@ impl NameGenerator { /// # Current implementation /// /// In current implementation, the function tries to get the name from the first - /// character of the name for the first type bound. - /// - /// If the name conflicts with existing generic parameters, it will try to - /// resolve the conflict with `for_unique_generic_name`. + /// uppercase character of the name for the first type bound. pub fn for_impl_trait_as_generic(&mut self, ty: &ast::ImplTraitType) -> SmolStr { let c = ty .type_bound_list() .and_then(|bounds| { let ty = bounds.bounds().next()?.ty()?; - ty.syntax().text().char_at(0.into()).filter(|ch| ch.is_alphabetic()) + ty.syntax().text().to_smolstr().chars().find(|ch| ch.is_uppercase()) }) .unwrap_or('T'); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs index 0a0ac8627c3d1..a0c4f9ccd9632 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs @@ -75,4 +75,39 @@ fn f(s: &S) { "#, ); } + + #[test] + fn regression_23175() { + check_diagnostics( + r#" +//- minicore: sized +trait Iterator { + type Item; + + fn foo(self) + where + Self: Sized, + { + } +} +impl Iterator for &mut T { + type Item = T::Item; +} + +trait Helper: + Iterator< + Item = [(); { + {} + 4 + }], +> +{ +} + +fn x(w: &mut dyn Helper) { + w.foo(); +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 7bc7955c4ec85..1265cdb21257a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -990,6 +990,21 @@ fn f(ty: Enum) { ); } + #[test] + fn builtin_cfg_pred_pat() { + check_diagnostics_no_bails( + r#" +//- minicore: cfg +fn f(ty: bool) { + match ty { + //^^ error: missing match arm: `false` not covered + cfg!(true) => (), + } +} +"#, + ); + } + #[test] fn unexpected_ty_fndef() { cov_mark::check!(validate_match_bailed_out); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs index ee2f6bf3192d4..8d6e9c4f713e3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs @@ -404,6 +404,67 @@ fn corge>(result: Result) -> T { let Ok(ok) = result; ok } +"#, + ); + } + + #[test] + fn regression_23327() { + check_diagnostics( + r#" +//- minicore: option +macro_rules! outer { + () => { + fn generated() { + inner!(); + } + }; +} +macro_rules! inner { + () => { + let None = Some(5); + }; +} +mod m { + outer!(); + //^^^^^^ error: non-exhaustive pattern: `Some(_)` not covered +} +"#, + ); + } + + #[test] + fn regression_23336_1() { + // Same as `regression_23336_2`, but with a struct that has a field. Its arity is what the + // usefulness analysis specializes against, so this reproduces the panic from the original + // report (`pat.rs:117`, index out of bounds) rather than the `PatCx::bug` one. + check_diagnostics( + r#" +struct S(u8); +fn main() { + let x: &((S, S),) = loop {}; + let ((ref mut r, _) | ((ref mut r, _),),) = &*x; + //^^^^^^^^^^^^^^^^^ error: expected (S, S), found (S,) + //^^^^^^^^^^^^^^ error: expected S, found ({unknown}, {unknown}) +} +"#, + ); + } + + #[test] + fn regression_23336_2() { + // The arms of the or-pattern have mismatching arities, which the usefulness analysis + // doesn't expect, so no diagnostic should be produced here. Here the struct has no fields, + // so the mismatched arity is caught by `PatCx::bug` instead of by an out-of-bounds index. + check_diagnostics( + r#" +struct S; +fn main() { + let x: &((S, S),) = loop {}; + let ((ref mut r, _) | ((ref mut r, _),),) = &*x; + //^^^^^^^^^^^^^^^^^ error: expected (S, S), found (S,) + //^^^^^^^^^^^^^^ error: expected S, found ({unknown}, {unknown}) +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 1e92a795e47a9..8e50cd5fc04ee 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -2020,6 +2020,22 @@ fn test(_: Result) { ); } + #[test] + fn type_match_cfg_macros() { + check_diagnostics( + r#" +//- minicore: cfg +fn main() { + let _: bool = cfg!(true); + match true { + cfg!(true) => (), + false => (), + } +} +"#, + ); + } + #[test] fn regression_23313() { check_diagnostics( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 6f5878071aaa8..703491478d574 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -371,6 +371,11 @@ fn hover_offset( let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?; render::closure_expr(sema, config, c, edition, display_target) }; + let cfg_predicate = || { + let predicate = token.parent().and_then(ast::CfgPredicate::cast)?; + render::cfg_predicate(sema, predicate) + .map(|markup| HoverResult { markup, actions: vec![] }) + }; let literal = || { render::literal(sema, original_token.clone(), display_target) .map(|markup| HoverResult { markup, actions: vec![] }) @@ -380,6 +385,7 @@ fn hover_offset( .or_else(rest_pat) .or_else(call) .or_else(closure) + .or_else(cfg_predicate) .or_else(literal) { res.push(result) 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 1897168821863..4be8d8a0cc29a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -829,6 +829,23 @@ struct DropInfo { has_dtor: Option, } +pub(super) fn cfg_predicate( + sema: &Semantics<'_, RootDatabase>, + predicate: ast::CfgPredicate, +) -> Option { + let krate = sema.scope(predicate.syntax())?.krate(); + let cfg = cfg::CfgExpr::parse_from_ast(predicate.clone()); + let evaluated = krate.cfg(sema.db).check(&cfg); + + let output = match evaluated { + Some(true) => "true", + Some(false) => "false", + None => "unknown", + }; + let s = format!("cfg predicate evaluated: `{output}` (`{predicate}`)"); + Some(s.into()) +} + pub(super) fn literal( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index c8aa8a2221640..0d8e3a1f89328 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -857,7 +857,7 @@ struct Foo$0(pub u32) where u32: Copy; ```rust struct Foo(pub u32) where - u32: Copy, + u32: Copy,; ``` --- @@ -1032,7 +1032,7 @@ pub struct Foo(pub i32, i32, pub(crate) i32); ``` ```rust - pub struct Foo(pub i32, /* … */) + pub struct Foo(pub i32, /* … */); ``` "#]], ); @@ -1547,7 +1547,7 @@ struct Foo$0 where u32: Copy; ```rust struct Foo where - u32: Copy, + u32: Copy,; ``` --- @@ -2205,7 +2205,7 @@ where ``` ```rust - pub struct Foo + pub struct Foo; ``` --- @@ -2923,7 +2923,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar + struct Bar; ``` --- @@ -2959,7 +2959,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar + struct Bar; ``` --- @@ -2988,7 +2988,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar + struct Bar; ``` --- @@ -3016,7 +3016,7 @@ pub struct B$0ar ``` ```rust - pub struct Bar + pub struct Bar; ``` --- @@ -3047,7 +3047,7 @@ pub struct B$0ar ``` ```rust - pub struct Bar + pub struct Bar; ``` --- @@ -3190,7 +3190,7 @@ struct S$0(core::marker::PhantomData); ``` ```rust - struct S(PhantomData) + struct S(PhantomData); ``` --- @@ -3352,7 +3352,7 @@ fn test_hover_layout_padding_info() { ``` ```rust - struct Foo(i16, u128, u64) + struct Foo(i16, u128, u64); ``` --- @@ -4719,7 +4719,7 @@ struct S$0T(T); ``` ```rust - struct ST(T) + struct ST(T); ``` --- @@ -4744,7 +4744,7 @@ struct S$0T(T); ``` ```rust - struct ST(T) + struct ST(T); ``` --- @@ -4770,7 +4770,7 @@ struct S$0T(T); ``` ```rust - struct ST(T) + struct ST(T); ``` --- @@ -6835,7 +6835,7 @@ pub fn gimme() -> theitem::TheItem { ``` ```rust - pub struct TheItem + pub struct TheItem; ``` --- @@ -6983,7 +6983,7 @@ mod string { ``` ```rust - struct String + struct String; ``` --- @@ -7651,7 +7651,7 @@ foo_macro!( ``` ```rust - pub struct Foo + pub struct Foo; ``` --- @@ -7676,7 +7676,7 @@ pub struct Foo(i32); ``` ```rust - pub struct Foo(i32) + pub struct Foo(i32); ``` --- @@ -7823,7 +7823,7 @@ pub struct Foo(T); ``` ```rust - pub struct Foo(T) + pub struct Foo(T); ``` --- @@ -7978,6 +7978,7 @@ impl T for () { ``` ```rust + impl T for () fn func() ``` @@ -8192,6 +8193,7 @@ fn f() { ``` ```rust + impl core::ops::Deref for Struct fn deref(&self) -> &Self::Target ``` "#]], @@ -8745,6 +8747,158 @@ foo!(r"{$0aaaaa}"); ); } +#[test] +fn hover_cfg_predicate() { + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0true)] +fn test() {} +"#, + expect![[r#" + *true* + cfg predicate evaluated: `true` (`true`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg(any($0true, false))] +fn test() {} +"#, + expect![[r#" + *true* + cfg predicate evaluated: `true` (`true`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg(any(true, $0false))] +fn test() {} +"#, + expect![[r#" + *false* + cfg predicate evaluated: `false` (`false`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0any(true, false))] +fn test() {} +"#, + expect![[r#" + *any* + cfg predicate evaluated: `true` (`any(true, false)`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0all(true, false))] +fn test() {} +"#, + expect![[r#" + *all* + cfg predicate evaluated: `false` (`all(true, false)`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0test)] +fn test() {} +"#, + expect![[r#" + *test* + cfg predicate evaluated: `true` (`test`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0opt_level = "2")] +fn test() {} +"#, + expect![[r#" + *opt_level* + cfg predicate evaluated: `true` (`opt_level = "2"`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0opt_level = "3")] +fn test() {} +"#, + expect![[r#" + *opt_level* + cfg predicate evaluated: `false` (`opt_level = "3"`) + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +macro_rules! identity { ($($t:tt)*) => { $($t)* }; } +identity! { + #[cfg($0test)] + fn test() {} +} +"#, + expect![[r#" + *test* + cfg predicate evaluated: `true` (`test`) + "#]], + ); + check( + r#" +//- /lib1.rs crate:dep cfg:opt_level=3 +#[macro_export] +macro_rules! expand_cfg { ($val:tt) => { #[cfg(opt_level = $val)] fn f(){} }; } +//- /main.rs crate:bin deps:dep cfg:test,dbg=false,opt_level=2 +dep::expand_cfg!("$02"); +"#, + expect![[r#" + *"2"* + cfg predicate evaluated: `true` (`opt_level="2"`) + "#]], + ); + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn test() { cfg!($0opt_level = "2") } +"#, + expect![[r#" + *opt_level* + cfg predicate evaluated: `true` (`opt_level="2"`) + "#]], + ); + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn test() { cfg!($0opt_level = "3") } +"#, + expect![[r#" + *opt_level* + cfg predicate evaluated: `false` (`opt_level="3"`) + "#]], + ); + check( + r#" +//- minicore: cfg +//- /main.rs cfg:test,dbg=false,opt_level=2 +fn test() { cfg!(and(false, $0opt_level = "2")) } +"#, + expect![[r#" + *opt_level* + cfg predicate evaluated: `true` (`opt_level="2"`) + "#]], + ); +} + #[test] fn method_call_without_parens() { check( @@ -9329,7 +9483,7 @@ impl Iterator for S { ``` ```rust - struct S + struct S; ``` "#]], ); @@ -10993,7 +11147,7 @@ struct NoDrop$0; ``` ```rust - struct NoDrop + struct NoDrop; ``` --- @@ -11017,7 +11171,7 @@ impl Drop for NeedsDrop { ``` ```rust - struct NeedsDrop + struct NeedsDrop; ``` --- @@ -11142,7 +11296,7 @@ struct Foo$0(T); ``` ```rust - struct Foo(T) + struct Foo(T); ``` --- @@ -11165,7 +11319,7 @@ struct Foo$0(T); ```rust struct Foo(T) where - T: Copy, + T: Copy,; ``` --- @@ -11191,7 +11345,7 @@ struct Foo$0(T::Assoc); ```rust struct Foo(::Assoc) where - T: Trait, + T: Trait,; ``` --- @@ -11361,6 +11515,56 @@ fn bar(v: &Foo) { ); } +#[test] +fn show_impl_container_without_type_params() { + check( + r#" +trait Trait { fn method(); } +impl Trait for Foo { fn method() {} } +struct Foo; + +fn main() { + Foo::$0method() +} + "#, + expect![[r#" + *method* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + impl Trait for Foo + fn method() + ``` + "#]], + ); + check( + r#" +//- minicore: clone, derive +#[derive(Clone)] +struct Foo; + +fn main() { + Foo::$0clone() +} + "#, + expect![[r#" + *clone* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + impl Clone for Foo + pub fn clone(&self) -> Self + ``` + "#]], + ); +} + #[test] fn extra_lifetime_param_on_trait_method_subst() { check( @@ -11387,6 +11591,7 @@ fn main() { ``` ```rust + impl ValueEnum for AudioFormat fn to_possible_value<'a>(&'a self) ``` "#]], @@ -11710,7 +11915,7 @@ doc_comment! { ``` ```rust - struct Bar + struct Bar; ``` --- @@ -11742,7 +11947,7 @@ struct Ba$0r; ``` ```rust - struct Bar + struct Bar; ``` --- @@ -11779,7 +11984,7 @@ Multiple lines of docs. ``` ```rust - struct Bar + struct Bar; ``` --- @@ -11813,7 +12018,7 @@ struct Ba$0r; ``` ```rust - struct Bar + struct Bar; ``` --- @@ -11846,7 +12051,7 @@ struct Ba$0r; ``` ```rust - struct Bar + struct Bar; ``` --- @@ -12065,3 +12270,34 @@ fn generic_tn(_: [T; N], _: impl Sized) {} "#]], ); } + +#[test] +fn hover_with_enum_closure_capture() { + check( + r#" +enum Request { + Resolve { url: () }, +} + +fn main() { + let event = Request::Resolve { url: () }; + let _handler = move || { + let Request::Resolve { url: _url } = event; + }; +} + +const FOO$0: () = main(); +"#, + expect![[r#" + *FOO* + + ```rust + ra_test_fixture + ``` + + ```rust + const FOO: () = () + ``` + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 7a01eb9054bfa..ac5c9da6c26a5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -31,6 +31,7 @@ pub struct StaticIndex<'a> { pub tokens: TokenStore, analysis: &'a Analysis, db: &'a RootDatabase, + include_hovers: bool, def_map: FxHashMap, TokenId>, } @@ -180,18 +181,22 @@ impl<'a> StaticIndex<'a> { syntax::NodeOrToken::Node(_) => None, syntax::NodeOrToken::Token(it) => Some(it), }); - let hover_config = HoverConfig { - links_in_hover: true, - memory_layout: None, - documentation: true, - keywords: true, - format: crate::HoverDocFormat::Markdown, - max_trait_assoc_items_count: None, - max_fields_count: Some(5), - max_enum_variants_count: Some(5), - max_subst_ty_len: SubstTyLen::Unlimited, - show_drop_glue: true, - ra_fixture: RaFixtureConfig::default(), + let hover_config = if self.include_hovers { + Some(HoverConfig { + links_in_hover: true, + memory_layout: None, + documentation: true, + keywords: true, + format: crate::HoverDocFormat::Markdown, + max_trait_assoc_items_count: None, + max_fields_count: Some(5), + max_enum_variants_count: Some(5), + max_subst_ty_len: SubstTyLen::Unlimited, + show_drop_glue: true, + ra_fixture: RaFixtureConfig::default(), + }) + } else { + None }; let mut result = StaticIndexedFile { file_id, folds, tokens: vec![] }; @@ -202,18 +207,20 @@ impl<'a> StaticIndex<'a> { let nav = def.try_to_nav(&sema).map(UpmappingResult::call_site); let it = self.tokens.insert(TokenStaticData { documentation: documentation_for_definition(&sema, def, scope_node), - hover: Some(hover_for_definition( - &sema, - file_id, - def, - None, - scope_node, - None, - false, - &hover_config, - edition, - display_target, - )), + hover: hover_config.as_ref().map(|hover_config| { + hover_for_definition( + &sema, + file_id, + def, + None, + scope_node, + None, + false, + hover_config, + edition, + display_target, + ) + }), definition: nav.as_ref().map(|it| FileRange { file_id: it.file_id, range: it.focus_or_full_range(), @@ -268,6 +275,7 @@ impl<'a> StaticIndex<'a> { pub fn compute( analysis: &'a Analysis, vendored_libs_config: VendoredLibrariesConfig<'_>, + include_hovers: bool, ) -> StaticIndex<'a> { let db = &analysis.db; hir::attach_db(db, || { @@ -290,6 +298,7 @@ impl<'a> StaticIndex<'a> { tokens: Default::default(), analysis, db, + include_hovers, def_map: Default::default(), }; let mut visited_files = FxHashSet::default(); @@ -378,7 +387,7 @@ mod tests { vendored_libs_config: VendoredLibrariesConfig<'_>, ) { let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); - let s = StaticIndex::compute(&analysis, vendored_libs_config); + let s = StaticIndex::compute(&analysis, vendored_libs_config, true); let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect(); for f in s.files { for (range, _) in f.tokens { @@ -404,7 +413,7 @@ mod tests { vendored_libs_config: VendoredLibrariesConfig<'_>, ) { let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); - let s = StaticIndex::compute(&analysis, vendored_libs_config); + let s = StaticIndex::compute(&analysis, vendored_libs_config, true); let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect(); for (_, t) in s.tokens.iter() { if let Some(t) = t.definition { @@ -429,7 +438,7 @@ mod tests { vendored_libs_config: VendoredLibrariesConfig<'_>, ) { let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture); - let s = StaticIndex::compute(&analysis, vendored_libs_config); + let s = StaticIndex::compute(&analysis, vendored_libs_config, true); let mut range_set: FxHashMap<_, i32> = ranges.iter().map(|it| (it.0, 0)).collect(); // Make sure that all references have at least one range. We use a HashMap instead of a diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs index acce7afb54e9b..aa0e945a2df94 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs @@ -65,7 +65,7 @@ fn cfg_attr_meta(p: &mut Parser<'_>, m: Marker) { const CFG_PREDICATE_FIRST_SET: TokenSet = TokenSet::new(&[T![true], T![false], T![ident]]); -fn cfg_predicate(p: &mut Parser<'_>) { +pub(super) fn cfg_predicate(p: &mut Parser<'_>) { let m = p.start(); if p.eat(T![true]) || p.eat(T![false]) { // test cfg_true_false_pred diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index 3df47bf0a429b..a62c9f1c1f653 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -69,6 +69,11 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) { }; if !p.at_ts(EXPR_FIRST) { + // FIXME: Trees are not ideal, but like #23477 will break things like MACRO_STMTS + + // test_err attr_end_of_stmt_list + // fn f() {#[attr]} + // fn f() {} p.err_and_bump("expected expression, item or let statement"); m.abandon(p); return; 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 275a79b96b148..d4147f38b2aaf 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 @@ -251,6 +251,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { // builtin#asm(""); // builtin#format_args("", 0, 1, a = 2 + 3, a + b); // builtin#offset_of(Foo, bar.baz.0); +// builtin#cfg(any(true, x = "y")); // } fn builtin_expr(p: &mut Parser<'_>) -> Option { let m = p.start(); @@ -322,6 +323,12 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option { // test include_bytes // fn foo() { builtin # include_bytes } Some(m.complete(p, INCLUDE_BYTES_EXPR)) + } else if p.eat_contextual_kw(T![cfg]) { + p.expect(T!['(']); + attributes::cfg_predicate(p); + p.eat(T![,]); + p.expect(T![')']); + Some(m.complete(p, CFG_PRED_EXPR)) } else { m.abandon(p); None diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs index ec4e595398788..d29e02cf79abb 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs @@ -91,7 +91,13 @@ pub(crate) fn variant_list(p: &mut Parser<'_>) { p.bump(T![_]); } else { m.abandon(p); - p.err_and_bump("expected enum variant"); + // test_err enum_variant_attrs_without_variant + // enum E {#[attr]} + if !p.at(T!['}']) { + p.err_and_bump("expected enum variant"); + } else { + p.error("expected enum variant"); + } return; } match p.current() { @@ -174,7 +180,14 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) { m.complete(p, RECORD_FIELD); } else { m.abandon(p); - p.err_and_bump("expected field declaration"); + // test_err record_field_attrs_without_field + // struct S {#[attr]} + // struct S {#[attr],} + if !p.at(T!['}']) { + p.err_and_bump("expected field declaration"); + } else { + p.error("expected field declaration"); + } } } } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs index fdc3f8c491eaf..ec0cc584ddb3d 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs @@ -222,6 +222,14 @@ fn builtin_pat(p: &mut Parser<'_>) -> Option { pattern(p); p.expect(T![')']); Some(m.complete(p, DEREF_PAT)) + } else if p.eat_contextual_kw(T![cfg]) { + // test cfg_pred_pat + // fn foo() { let builtin # cfg(any(true, x = "y")) = true; } + p.expect(T!['(']); + attributes::cfg_predicate(p); + p.eat(T![,]); + p.expect(T![')']); + Some(m.complete(p, CFG_PRED_PAT)) } else { m.abandon(p); None diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs index 7311947525ed9..c7a09ba95d077 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs @@ -36,4 +36,19 @@ impl SyntaxKind { // Assuming no edition removed keywords... self == SyntaxKind::IDENT || self.is_keyword(Edition::LATEST) } + + #[inline] + pub fn is_l_delimiter(self) -> bool { + matches!(self, SyntaxKind::L_CURLY | Self::L_PAREN | Self::L_BRACK) + } + + #[inline] + pub fn is_r_delimiter(self) -> bool { + matches!(self, SyntaxKind::R_CURLY | Self::R_PAREN | Self::R_BRACK) + } + + #[inline] + pub fn is_delimiter(self) -> bool { + self.is_l_delimiter() || self.is_r_delimiter() + } } 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 81a3b423e4436..3f1700460235d 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 @@ -199,6 +199,8 @@ pub enum SyntaxKind { CFG_ATTR_META, CFG_COMPOSITE, CFG_META, + CFG_PRED_EXPR, + CFG_PRED_PAT, CLOSURE_EXPR, CONST, CONST_ARG, @@ -387,6 +389,8 @@ impl SyntaxKind { | CFG_ATTR_META | CFG_COMPOSITE | CFG_META + | CFG_PRED_EXPR + | CFG_PRED_PAT | CLOSURE_EXPR | CONST | CONST_ARG diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index 9e3de7517f227..71e536c35896b 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -107,6 +107,8 @@ mod ok { #[test] fn cfg_meta() { run_and_expect_no_errors("test_data/parser/inline/ok/cfg_meta.rs"); } #[test] + fn cfg_pred_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/cfg_pred_pat.rs"); } + #[test] fn cfg_true_false_pred() { run_and_expect_no_errors("test_data/parser/inline/ok/cfg_true_false_pred.rs"); } @@ -823,6 +825,10 @@ mod err { run_and_expect_errors("test_data/parser/inline/err/async_without_semicolon.rs"); } #[test] + fn attr_end_of_stmt_list() { + run_and_expect_errors("test_data/parser/inline/err/attr_end_of_stmt_list.rs"); + } + #[test] fn bad_asm_expr() { run_and_expect_errors("test_data/parser/inline/err/bad_asm_expr.rs"); } #[test] fn closure_ret_recovery() { @@ -849,6 +855,10 @@ mod err { #[test] fn empty_segment() { run_and_expect_errors("test_data/parser/inline/err/empty_segment.rs"); } #[test] + fn enum_variant_attrs_without_variant() { + run_and_expect_errors("test_data/parser/inline/err/enum_variant_attrs_without_variant.rs"); + } + #[test] fn fn_pointer_type_missing_fn() { run_and_expect_errors("test_data/parser/inline/err/fn_pointer_type_missing_fn.rs"); } @@ -954,6 +964,10 @@ mod err { #[test] fn pub_expr() { run_and_expect_errors("test_data/parser/inline/err/pub_expr.rs"); } #[test] + fn record_field_attrs_without_field() { + run_and_expect_errors("test_data/parser/inline/err/record_field_attrs_without_field.rs"); + } + #[test] fn record_literal_before_ellipsis_recovery() { run_and_expect_errors( "test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs", diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rast new file mode 100644 index 0000000000000..c58ebcfaefe98 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rast @@ -0,0 +1,41 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + ATTR + POUND "#" + L_BRACK "[" + PATH_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "attr" + R_BRACK "]" + ERROR + R_CURLY "}" + WHITESPACE "\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" +error 15: expected expression, item or let statement +error 26: expected R_CURLY diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rs new file mode 100644 index 0000000000000..17de9b3627ca8 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/attr_end_of_stmt_list.rs @@ -0,0 +1,2 @@ +fn f() {#[attr]} +fn f() {} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rast new file mode 100644 index 0000000000000..70bc52be1a40e --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rast @@ -0,0 +1,21 @@ +SOURCE_FILE + ENUM + ENUM_KW "enum" + WHITESPACE " " + NAME + IDENT "E" + WHITESPACE " " + VARIANT_LIST + L_CURLY "{" + ATTR + POUND "#" + L_BRACK "[" + PATH_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "attr" + R_BRACK "]" + R_CURLY "}" + WHITESPACE "\n" +error 15: expected enum variant diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rs new file mode 100644 index 0000000000000..a3fcbd1190b1b --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/enum_variant_attrs_without_variant.rs @@ -0,0 +1 @@ +enum E {#[attr]} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rast new file mode 100644 index 0000000000000..1aad3e3dd64b7 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rast @@ -0,0 +1,43 @@ +SOURCE_FILE + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "S" + WHITESPACE " " + RECORD_FIELD_LIST + L_CURLY "{" + ATTR + POUND "#" + L_BRACK "[" + PATH_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "attr" + R_BRACK "]" + R_CURLY "}" + WHITESPACE "\n" + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "S" + WHITESPACE " " + RECORD_FIELD_LIST + L_CURLY "{" + ATTR + POUND "#" + L_BRACK "[" + PATH_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "attr" + R_BRACK "]" + ERROR + COMMA "," + R_CURLY "}" + WHITESPACE "\n" +error 17: expected field declaration +error 36: expected field declaration diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rs new file mode 100644 index 0000000000000..cfa5a725bc1b7 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_field_attrs_without_field.rs @@ -0,0 +1,2 @@ +struct S {#[attr]} +struct S {#[attr],} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast index 19a84ac540961..b415735277190 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast @@ -100,6 +100,29 @@ SOURCE_FILE INT_NUMBER "0" R_PAREN ")" SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + CFG_PRED_EXPR + BUILTIN_KW "builtin" + POUND "#" + CFG_KW "cfg" + L_PAREN "(" + CFG_COMPOSITE + IDENT "any" + L_PAREN "(" + CFG_ATOM + TRUE_KW "true" + COMMA "," + WHITESPACE " " + CFG_ATOM + IDENT "x" + WHITESPACE " " + EQ "=" + WHITESPACE " " + STRING "\"y\"" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs index 920d0f794f242..302cf4196a6f6 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs @@ -2,4 +2,5 @@ fn foo() { builtin#asm(""); builtin#format_args("", 0, 1, a = 2 + 3, a + b); builtin#offset_of(Foo, bar.baz.0); + builtin#cfg(any(true, x = "y")); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rast new file mode 100644 index 0000000000000..69e348974f7fc --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rast @@ -0,0 +1,48 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LET_STMT + LET_KW "let" + WHITESPACE " " + CFG_PRED_PAT + BUILTIN_KW "builtin" + WHITESPACE " " + POUND "#" + WHITESPACE " " + CFG_KW "cfg" + L_PAREN "(" + CFG_COMPOSITE + IDENT "any" + L_PAREN "(" + CFG_ATOM + TRUE_KW "true" + COMMA "," + WHITESPACE " " + CFG_ATOM + IDENT "x" + WHITESPACE " " + EQ "=" + WHITESPACE " " + STRING "\"y\"" + R_PAREN ")" + R_PAREN ")" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + TRUE_KW "true" + SEMICOLON ";" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rs new file mode 100644 index 0000000000000..52f367cec3ef1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/cfg_pred_pat.rs @@ -0,0 +1 @@ +fn foo() { let builtin # cfg(any(true, x = "y")) = true; } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index 44e19f2d3c3bf..a9361e00f6207 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -14,7 +14,7 @@ publish = false doctest = false [dependencies] -proc-macro-srv.workspace = true +proc-macro-srv = { workspace = true, optional = true } proc-macro-api.workspace = true clap = {version = "4.5.42", default-features = false, features = ["std"]} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 0427d0ee74a6e..ada88d2568bfc 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -13,12 +13,12 @@ rust-version.workspace = true doctest = false [dependencies] -paths.workspace = true -rustc-hash.workspace = true +paths = { workspace = true, optional = true } +rustc-hash = { workspace = true, optional = true } # span = {workspace = true, default-features = false} does not work -span = { path = "../span", version = "0.0.0", default-features = false} -intern.workspace = true -stdx.workspace = true +span = { path = "../span", version = "0.0.0", default-features = false, optional = true } +intern = { workspace = true, optional = true } +stdx = { workspace = true, optional = true } [dev-dependencies] expect-test.workspace = true @@ -29,7 +29,7 @@ proc-macro-test.path = "./proc-macro-test" [features] default = [] -in-rust-tree = [] +in-rust-tree = ["dep:intern", "dep:paths", "dep:rustc-hash", "dep:span", "dep:stdx"] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index 2cb6817fb9167..7b3752f2a1a45 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -31,23 +31,23 @@ fn test_derive_empty() { IDENT 1 r#u32 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..6#0 struct - IDENT 42:Root[0000, 0]@7..8#0 S - GROUP {} 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@46..47#0 42:Root[0000, 0]@9..47#0 - IDENT 42:Root[0000, 0]@11..16#0 field - PUNCT 42:Root[0000, 0]@16..17#0 : [alone] - PUNCT 42:Root[0000, 0]@18..19#0 & [joint] - PUNCT 42:Root[0000, 0]@22..23#0 ' [joint] - IDENT 42:Root[0000, 0]@22..24#0 r#lt - IDENT 42:Root[0000, 0]@25..27#0 fn - GROUP () 42:Root[0000, 0]@27..28#0 42:Root[0000, 0]@31..32#0 42:Root[0000, 0]@27..32#0 - IDENT 42:Root[0000, 0]@28..31#0 u32 - PUNCT 42:Root[0000, 0]@33..34#0 - [joint] - PUNCT 42:Root[0000, 0]@34..35#0 > [alone] - PUNCT 42:Root[0000, 0]@36..37#0 & [joint] - PUNCT 42:Root[0000, 0]@38..39#0 ' [joint] - IDENT 42:Root[0000, 0]@38..39#0 a - IDENT 42:Root[0000, 0]@42..45#0 r#u32 + IDENT 42:Root[0000, 0]@0..6#1 struct + IDENT 42:Root[0000, 0]@7..8#1 S + GROUP {} 42:Root[0000, 0]@9..10#1 42:Root[0000, 0]@46..47#1 42:Root[0000, 0]@9..47#1 + IDENT 42:Root[0000, 0]@11..16#1 field + PUNCT 42:Root[0000, 0]@16..17#1 : [alone] + PUNCT 42:Root[0000, 0]@18..19#1 & [joint] + PUNCT 42:Root[0000, 0]@22..23#1 ' [joint] + IDENT 42:Root[0000, 0]@22..24#1 r#lt + IDENT 42:Root[0000, 0]@25..27#1 fn + GROUP () 42:Root[0000, 0]@27..28#1 42:Root[0000, 0]@31..32#1 42:Root[0000, 0]@27..32#1 + IDENT 42:Root[0000, 0]@28..31#1 u32 + PUNCT 42:Root[0000, 0]@33..34#1 - [joint] + PUNCT 42:Root[0000, 0]@34..35#1 > [alone] + PUNCT 42:Root[0000, 0]@36..37#1 & [joint] + PUNCT 42:Root[0000, 0]@38..39#1 ' [joint] + IDENT 42:Root[0000, 0]@38..39#1 a + IDENT 42:Root[0000, 0]@42..45#1 r#u32 "#]], ); } @@ -137,76 +137,76 @@ pub struct Foo { PUNCT 1 , [alone] "#]], expect![[r#" - PUNCT 42:Root[0000, 0]@1..2#0 # [joint] - GROUP [] 42:Root[0000, 0]@2..3#0 42:Root[0000, 0]@52..53#0 42:Root[0000, 0]@2..53#0 - IDENT 42:Root[0000, 0]@3..9#0 helper - GROUP () 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@51..52#0 42:Root[0000, 0]@9..52#0 - IDENT 42:Root[0000, 0]@10..18#0 build_fn - GROUP () 42:Root[0000, 0]@18..19#0 42:Root[0000, 0]@50..51#0 42:Root[0000, 0]@18..51#0 - IDENT 42:Root[0000, 0]@19..26#0 private - PUNCT 42:Root[0000, 0]@26..27#0 , [alone] - IDENT 42:Root[0000, 0]@28..32#0 name - PUNCT 42:Root[0000, 0]@33..34#0 = [alone] - LITER 42:Root[0000, 0]@35..50#0 Str partial_build - IDENT 42:Root[0000, 0]@54..57#0 pub - IDENT 42:Root[0000, 0]@58..64#0 struct - IDENT 42:Root[0000, 0]@65..68#0 Foo - GROUP {} 42:Root[0000, 0]@69..70#0 42:Root[0000, 0]@190..191#0 42:Root[0000, 0]@69..191#0 - PUNCT 42:Root[0000, 0]@0..0#0 # [alone] - GROUP [] 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 - IDENT 42:Root[0000, 0]@0..0#0 doc - PUNCT 42:Root[0000, 0]@0..0#0 = [alone] - LITER 42:Root[0000, 0]@75..130#0 Str The domain where this federated instance is running - PUNCT 42:Root[0000, 0]@135..136#0 # [joint] - GROUP [] 42:Root[0000, 0]@136..137#0 42:Root[0000, 0]@157..158#0 42:Root[0000, 0]@136..158#0 - IDENT 42:Root[0000, 0]@137..143#0 helper - GROUP () 42:Root[0000, 0]@143..144#0 42:Root[0000, 0]@156..157#0 42:Root[0000, 0]@143..157#0 - IDENT 42:Root[0000, 0]@144..150#0 setter - GROUP () 42:Root[0000, 0]@150..151#0 42:Root[0000, 0]@155..156#0 42:Root[0000, 0]@150..156#0 - IDENT 42:Root[0000, 0]@151..155#0 into - IDENT 42:Root[0000, 0]@163..166#0 pub - GROUP () 42:Root[0000, 0]@166..167#0 42:Root[0000, 0]@172..173#0 42:Root[0000, 0]@166..173#0 - IDENT 42:Root[0000, 0]@167..172#0 crate - IDENT 42:Root[0000, 0]@174..180#0 domain - PUNCT 42:Root[0000, 0]@180..181#0 : [alone] - IDENT 42:Root[0000, 0]@182..188#0 String - PUNCT 42:Root[0000, 0]@188..189#0 , [alone] - - - PUNCT 42:Root[0000, 0]@1..2#0 # [joint] - GROUP [] 42:Root[0000, 0]@2..3#0 42:Root[0000, 0]@52..53#0 42:Root[0000, 0]@2..53#0 - IDENT 42:Root[0000, 0]@3..9#0 helper - GROUP () 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@51..52#0 42:Root[0000, 0]@9..52#0 - IDENT 42:Root[0000, 0]@10..18#0 build_fn - GROUP () 42:Root[0000, 0]@18..19#0 42:Root[0000, 0]@50..51#0 42:Root[0000, 0]@18..51#0 - IDENT 42:Root[0000, 0]@19..26#0 private - PUNCT 42:Root[0000, 0]@26..27#0 , [alone] - IDENT 42:Root[0000, 0]@28..32#0 name - PUNCT 42:Root[0000, 0]@33..34#0 = [alone] - LITER 42:Root[0000, 0]@35..50#0 Str partial_build - IDENT 42:Root[0000, 0]@54..57#0 pub - IDENT 42:Root[0000, 0]@58..64#0 struct - IDENT 42:Root[0000, 0]@65..68#0 Foo - GROUP {} 42:Root[0000, 0]@69..70#0 42:Root[0000, 0]@190..191#0 42:Root[0000, 0]@69..191#0 - PUNCT 42:Root[0000, 0]@0..0#0 # [alone] - GROUP [] 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 - IDENT 42:Root[0000, 0]@0..0#0 doc - PUNCT 42:Root[0000, 0]@0..0#0 = [alone] - LITER 42:Root[0000, 0]@75..130#0 Str The domain where this federated instance is running - PUNCT 42:Root[0000, 0]@135..136#0 # [joint] - GROUP [] 42:Root[0000, 0]@136..137#0 42:Root[0000, 0]@157..158#0 42:Root[0000, 0]@136..158#0 - IDENT 42:Root[0000, 0]@137..143#0 helper - GROUP () 42:Root[0000, 0]@143..144#0 42:Root[0000, 0]@156..157#0 42:Root[0000, 0]@143..157#0 - IDENT 42:Root[0000, 0]@144..150#0 setter - GROUP () 42:Root[0000, 0]@150..151#0 42:Root[0000, 0]@155..156#0 42:Root[0000, 0]@150..156#0 - IDENT 42:Root[0000, 0]@151..155#0 into - IDENT 42:Root[0000, 0]@163..166#0 pub - GROUP () 42:Root[0000, 0]@166..167#0 42:Root[0000, 0]@172..173#0 42:Root[0000, 0]@166..173#0 - IDENT 42:Root[0000, 0]@167..172#0 crate - IDENT 42:Root[0000, 0]@174..180#0 domain - PUNCT 42:Root[0000, 0]@180..181#0 : [alone] - IDENT 42:Root[0000, 0]@182..188#0 String - PUNCT 42:Root[0000, 0]@188..189#0 , [alone] + PUNCT 42:Root[0000, 0]@1..2#1 # [joint] + GROUP [] 42:Root[0000, 0]@2..3#1 42:Root[0000, 0]@52..53#1 42:Root[0000, 0]@2..53#1 + IDENT 42:Root[0000, 0]@3..9#1 helper + GROUP () 42:Root[0000, 0]@9..10#1 42:Root[0000, 0]@51..52#1 42:Root[0000, 0]@9..52#1 + IDENT 42:Root[0000, 0]@10..18#1 build_fn + GROUP () 42:Root[0000, 0]@18..19#1 42:Root[0000, 0]@50..51#1 42:Root[0000, 0]@18..51#1 + IDENT 42:Root[0000, 0]@19..26#1 private + PUNCT 42:Root[0000, 0]@26..27#1 , [alone] + IDENT 42:Root[0000, 0]@28..32#1 name + PUNCT 42:Root[0000, 0]@33..34#1 = [alone] + LITER 42:Root[0000, 0]@35..50#1 Str partial_build + IDENT 42:Root[0000, 0]@54..57#1 pub + IDENT 42:Root[0000, 0]@58..64#1 struct + IDENT 42:Root[0000, 0]@65..68#1 Foo + GROUP {} 42:Root[0000, 0]@69..70#1 42:Root[0000, 0]@190..191#1 42:Root[0000, 0]@69..191#1 + PUNCT 42:Root[0000, 0]@0..0#1 # [alone] + GROUP [] 42:Root[0000, 0]@0..0#1 42:Root[0000, 0]@0..0#1 42:Root[0000, 0]@0..0#1 + IDENT 42:Root[0000, 0]@0..0#1 doc + PUNCT 42:Root[0000, 0]@0..0#1 = [alone] + LITER 42:Root[0000, 0]@75..130#1 Str The domain where this federated instance is running + PUNCT 42:Root[0000, 0]@135..136#1 # [joint] + GROUP [] 42:Root[0000, 0]@136..137#1 42:Root[0000, 0]@157..158#1 42:Root[0000, 0]@136..158#1 + IDENT 42:Root[0000, 0]@137..143#1 helper + GROUP () 42:Root[0000, 0]@143..144#1 42:Root[0000, 0]@156..157#1 42:Root[0000, 0]@143..157#1 + IDENT 42:Root[0000, 0]@144..150#1 setter + GROUP () 42:Root[0000, 0]@150..151#1 42:Root[0000, 0]@155..156#1 42:Root[0000, 0]@150..156#1 + IDENT 42:Root[0000, 0]@151..155#1 into + IDENT 42:Root[0000, 0]@163..166#1 pub + GROUP () 42:Root[0000, 0]@166..167#1 42:Root[0000, 0]@172..173#1 42:Root[0000, 0]@166..173#1 + IDENT 42:Root[0000, 0]@167..172#1 crate + IDENT 42:Root[0000, 0]@174..180#1 domain + PUNCT 42:Root[0000, 0]@180..181#1 : [alone] + IDENT 42:Root[0000, 0]@182..188#1 String + PUNCT 42:Root[0000, 0]@188..189#1 , [alone] + + + PUNCT 42:Root[0000, 0]@1..2#1 # [joint] + GROUP [] 42:Root[0000, 0]@2..3#1 42:Root[0000, 0]@52..53#1 42:Root[0000, 0]@2..53#1 + IDENT 42:Root[0000, 0]@3..9#1 helper + GROUP () 42:Root[0000, 0]@9..10#1 42:Root[0000, 0]@51..52#1 42:Root[0000, 0]@9..52#1 + IDENT 42:Root[0000, 0]@10..18#1 build_fn + GROUP () 42:Root[0000, 0]@18..19#1 42:Root[0000, 0]@50..51#1 42:Root[0000, 0]@18..51#1 + IDENT 42:Root[0000, 0]@19..26#1 private + PUNCT 42:Root[0000, 0]@26..27#1 , [alone] + IDENT 42:Root[0000, 0]@28..32#1 name + PUNCT 42:Root[0000, 0]@33..34#1 = [alone] + LITER 42:Root[0000, 0]@35..50#1 Str partial_build + IDENT 42:Root[0000, 0]@54..57#1 pub + IDENT 42:Root[0000, 0]@58..64#1 struct + IDENT 42:Root[0000, 0]@65..68#1 Foo + GROUP {} 42:Root[0000, 0]@69..70#1 42:Root[0000, 0]@190..191#1 42:Root[0000, 0]@69..191#1 + PUNCT 42:Root[0000, 0]@0..0#1 # [alone] + GROUP [] 42:Root[0000, 0]@0..0#1 42:Root[0000, 0]@0..0#1 42:Root[0000, 0]@0..0#1 + IDENT 42:Root[0000, 0]@0..0#1 doc + PUNCT 42:Root[0000, 0]@0..0#1 = [alone] + LITER 42:Root[0000, 0]@75..130#1 Str The domain where this federated instance is running + PUNCT 42:Root[0000, 0]@135..136#1 # [joint] + GROUP [] 42:Root[0000, 0]@136..137#1 42:Root[0000, 0]@157..158#1 42:Root[0000, 0]@136..158#1 + IDENT 42:Root[0000, 0]@137..143#1 helper + GROUP () 42:Root[0000, 0]@143..144#1 42:Root[0000, 0]@156..157#1 42:Root[0000, 0]@143..157#1 + IDENT 42:Root[0000, 0]@144..150#1 setter + GROUP () 42:Root[0000, 0]@150..151#1 42:Root[0000, 0]@155..156#1 42:Root[0000, 0]@150..156#1 + IDENT 42:Root[0000, 0]@151..155#1 into + IDENT 42:Root[0000, 0]@163..166#1 pub + GROUP () 42:Root[0000, 0]@166..167#1 42:Root[0000, 0]@172..173#1 42:Root[0000, 0]@166..173#1 + IDENT 42:Root[0000, 0]@167..172#1 crate + IDENT 42:Root[0000, 0]@174..180#1 domain + PUNCT 42:Root[0000, 0]@180..181#1 : [alone] + IDENT 42:Root[0000, 0]@182..188#1 String + PUNCT 42:Root[0000, 0]@188..189#1 , [alone] "#]], ); } @@ -232,19 +232,19 @@ fn test_derive_error() { PUNCT 1 ; [alone] "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..6#0 struct - IDENT 42:Root[0000, 0]@7..8#0 S - GROUP {} 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@22..23#0 42:Root[0000, 0]@9..23#0 - IDENT 42:Root[0000, 0]@11..16#0 field - PUNCT 42:Root[0000, 0]@16..17#0 : [alone] - IDENT 42:Root[0000, 0]@18..21#0 u32 - - - IDENT 42:Root[0000, 0]@0..13#0 compile_error - PUNCT 42:Root[0000, 0]@13..14#0 ! [joint] - GROUP () 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@62..63#0 42:Root[0000, 0]@14..63#0 - LITER 42:Root[0000, 0]@15..62#0 Str #[derive(DeriveError)] struct S {field : u32} - PUNCT 42:Root[0000, 0]@63..64#0 ; [alone] + IDENT 42:Root[0000, 0]@0..6#1 struct + IDENT 42:Root[0000, 0]@7..8#1 S + GROUP {} 42:Root[0000, 0]@9..10#1 42:Root[0000, 0]@22..23#1 42:Root[0000, 0]@9..23#1 + IDENT 42:Root[0000, 0]@11..16#1 field + PUNCT 42:Root[0000, 0]@16..17#1 : [alone] + IDENT 42:Root[0000, 0]@18..21#1 u32 + + + IDENT 42:Root[0000, 0]@0..13#1 compile_error + PUNCT 42:Root[0000, 0]@13..14#1 ! [joint] + GROUP () 42:Root[0000, 0]@14..15#1 42:Root[0000, 0]@62..63#1 42:Root[0000, 0]@14..63#1 + LITER 42:Root[0000, 0]@15..62#1 Str #[derive(DeriveError)] struct S {field : u32} + PUNCT 42:Root[0000, 0]@63..64#1 ; [alone] "#]], ); } @@ -273,22 +273,22 @@ fn test_fn_like_macro_noop() { GROUP [] 1 1 1 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..5#0 ident - PUNCT 42:Root[0000, 0]@5..6#0 , [alone] - LITER 42:Root[0000, 0]@7..8#0 Integer 0 - PUNCT 42:Root[0000, 0]@8..9#0 , [alone] - LITER 42:Root[0000, 0]@10..11#0 Integer 1 - PUNCT 42:Root[0000, 0]@11..12#0 , [alone] - GROUP [] 42:Root[0000, 0]@13..14#0 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@13..15#0 - - - IDENT 42:Root[0000, 0]@0..5#0 ident - PUNCT 42:Root[0000, 0]@5..6#0 , [alone] - LITER 42:Root[0000, 0]@7..8#0 Integer 0 - PUNCT 42:Root[0000, 0]@8..9#0 , [alone] - LITER 42:Root[0000, 0]@10..11#0 Integer 1 - PUNCT 42:Root[0000, 0]@11..12#0 , [alone] - GROUP [] 42:Root[0000, 0]@13..14#0 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@13..15#0 + IDENT 42:Root[0000, 0]@0..5#1 ident + PUNCT 42:Root[0000, 0]@5..6#1 , [alone] + LITER 42:Root[0000, 0]@7..8#1 Integer 0 + PUNCT 42:Root[0000, 0]@8..9#1 , [alone] + LITER 42:Root[0000, 0]@10..11#1 Integer 1 + PUNCT 42:Root[0000, 0]@11..12#1 , [alone] + GROUP [] 42:Root[0000, 0]@13..14#1 42:Root[0000, 0]@14..15#1 42:Root[0000, 0]@13..15#1 + + + IDENT 42:Root[0000, 0]@0..5#1 ident + PUNCT 42:Root[0000, 0]@5..6#1 , [alone] + LITER 42:Root[0000, 0]@7..8#1 Integer 0 + PUNCT 42:Root[0000, 0]@8..9#1 , [alone] + LITER 42:Root[0000, 0]@10..11#1 Integer 1 + PUNCT 42:Root[0000, 0]@11..12#1 , [alone] + GROUP [] 42:Root[0000, 0]@13..14#1 42:Root[0000, 0]@14..15#1 42:Root[0000, 0]@13..15#1 "#]], ); } @@ -315,20 +315,20 @@ fn test_fn_like_macro_clone_ident_subtree() { IDENT 1 ident3 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..5#0 ident - PUNCT 42:Root[0000, 0]@5..6#0 , [alone] - GROUP [] 42:Root[0000, 0]@7..8#0 42:Root[0000, 0]@22..23#0 42:Root[0000, 0]@7..23#0 - IDENT 42:Root[0000, 0]@8..14#0 ident2 - PUNCT 42:Root[0000, 0]@14..15#0 , [alone] - IDENT 42:Root[0000, 0]@16..22#0 ident3 - - - IDENT 42:Root[0000, 0]@0..5#0 ident - PUNCT 42:Root[0000, 0]@5..6#0 , [alone] - GROUP [] 42:Root[0000, 0]@7..23#0 42:Root[0000, 0]@7..23#0 42:Root[0000, 0]@7..23#0 - IDENT 42:Root[0000, 0]@8..14#0 ident2 - PUNCT 42:Root[0000, 0]@14..15#0 , [alone] - IDENT 42:Root[0000, 0]@16..22#0 ident3 + IDENT 42:Root[0000, 0]@0..5#1 ident + PUNCT 42:Root[0000, 0]@5..6#1 , [alone] + GROUP [] 42:Root[0000, 0]@7..8#1 42:Root[0000, 0]@22..23#1 42:Root[0000, 0]@7..23#1 + IDENT 42:Root[0000, 0]@8..14#1 ident2 + PUNCT 42:Root[0000, 0]@14..15#1 , [alone] + IDENT 42:Root[0000, 0]@16..22#1 ident3 + + + IDENT 42:Root[0000, 0]@0..5#1 ident + PUNCT 42:Root[0000, 0]@5..6#1 , [alone] + GROUP [] 42:Root[0000, 0]@7..23#1 42:Root[0000, 0]@7..23#1 42:Root[0000, 0]@7..23#1 + IDENT 42:Root[0000, 0]@8..14#1 ident2 + PUNCT 42:Root[0000, 0]@14..15#1 , [alone] + IDENT 42:Root[0000, 0]@16..22#1 ident3 "#]], ); } @@ -345,10 +345,10 @@ fn test_fn_like_macro_clone_raw_ident() { IDENT 1 r#async "#]], expect![[r#" - IDENT 42:Root[0000, 0]@2..7#0 r#async + IDENT 42:Root[0000, 0]@2..7#1 r#async - IDENT 42:Root[0000, 0]@2..7#0 r#async + IDENT 42:Root[0000, 0]@2..7#1 r#async "#]], ); } @@ -366,11 +366,11 @@ fn test_fn_like_fn_like_span_join() { IDENT 1 r#joined "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..3#0 foo - IDENT 42:Root[0000, 0]@8..11#0 bar + IDENT 42:Root[0000, 0]@0..3#1 foo + IDENT 42:Root[0000, 0]@8..11#1 bar - IDENT 42:Root[0000, 0]@0..11#0 r#joined + IDENT 42:Root[0000, 0]@0..11#1 r#joined "#]], ); } @@ -391,14 +391,14 @@ fn test_fn_like_fn_like_span_ops() { IDENT 1 start_span "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..12#0 set_def_site - IDENT 42:Root[0000, 0]@13..33#0 resolved_at_def_site - IDENT 42:Root[0000, 0]@34..44#0 start_span + IDENT 42:Root[0000, 0]@0..12#1 set_def_site + IDENT 42:Root[0000, 0]@13..33#1 resolved_at_def_site + IDENT 42:Root[0000, 0]@34..44#1 start_span - IDENT 41:Root[0000, 0]@0..150#0 set_def_site - IDENT 42:Root[0000, 0]@13..33#0 resolved_at_def_site - IDENT 42:Root[0000, 0]@34..34#0 start_span + IDENT 41:Root[0000, 0]@0..150#1 set_def_site + IDENT 42:Root[0000, 0]@13..33#1 resolved_at_def_site + IDENT 42:Root[0000, 0]@34..34#1 start_span "#]], ); } @@ -428,19 +428,19 @@ fn test_fn_like_mk_literals() { expect![[r#" - LITER 42:Root[0000, 0]@0..100#0 ByteStr byte_string - LITER 42:Root[0000, 0]@0..100#0 Char c - LITER 42:Root[0000, 0]@0..100#0 Str string - LITER 42:Root[0000, 0]@0..100#0 Str -string - LITER 42:Root[0000, 0]@0..100#0 CStr cstring - LITER 42:Root[0000, 0]@0..100#0 Float 3.14f64 - LITER 42:Root[0000, 0]@0..100#0 Float -3.14f64 - LITER 42:Root[0000, 0]@0..100#0 Float 3.14 - LITER 42:Root[0000, 0]@0..100#0 Float -3.14 - LITER 42:Root[0000, 0]@0..100#0 Integer 123i64 - LITER 42:Root[0000, 0]@0..100#0 Integer -123i64 - LITER 42:Root[0000, 0]@0..100#0 Integer 123 - LITER 42:Root[0000, 0]@0..100#0 Integer -123 + LITER 42:Root[0000, 0]@0..100#1 ByteStr byte_string + LITER 42:Root[0000, 0]@0..100#1 Char c + LITER 42:Root[0000, 0]@0..100#1 Str string + LITER 42:Root[0000, 0]@0..100#1 Str -string + LITER 42:Root[0000, 0]@0..100#1 CStr cstring + LITER 42:Root[0000, 0]@0..100#1 Float 3.14f64 + LITER 42:Root[0000, 0]@0..100#1 Float -3.14f64 + LITER 42:Root[0000, 0]@0..100#1 Float 3.14 + LITER 42:Root[0000, 0]@0..100#1 Float -3.14 + LITER 42:Root[0000, 0]@0..100#1 Integer 123i64 + LITER 42:Root[0000, 0]@0..100#1 Integer -123i64 + LITER 42:Root[0000, 0]@0..100#1 Integer 123 + LITER 42:Root[0000, 0]@0..100#1 Integer -123 "#]], ); } @@ -459,8 +459,8 @@ fn test_fn_like_mk_idents() { expect![[r#" - IDENT 42:Root[0000, 0]@0..100#0 standard - IDENT 42:Root[0000, 0]@0..100#0 r#raw + IDENT 42:Root[0000, 0]@0..100#1 standard + IDENT 42:Root[0000, 0]@0..100#1 r#raw "#]], ); } @@ -515,48 +515,48 @@ fn test_fn_like_macro_clone_literals() { LITER 1 CStr null "#]], expect![[r#" - LITER 42:Root[0000, 0]@0..4#0 Integer 1u16 - PUNCT 42:Root[0000, 0]@4..5#0 , [alone] - LITER 42:Root[0000, 0]@6..11#0 Integer 2_u32 - PUNCT 42:Root[0000, 0]@11..12#0 , [alone] - PUNCT 42:Root[0000, 0]@13..14#0 - [alone] - LITER 42:Root[0000, 0]@14..18#0 Integer 4i64 - PUNCT 42:Root[0000, 0]@18..19#0 , [alone] - LITER 42:Root[0000, 0]@20..27#0 Float 3.14f32 - PUNCT 42:Root[0000, 0]@27..28#0 , [alone] - LITER 42:Root[0000, 0]@29..43#0 Str hello bridge - PUNCT 42:Root[0000, 0]@43..44#0 , [alone] - LITER 42:Root[0000, 0]@45..61#0 Str suffixedsuffix - PUNCT 42:Root[0000, 0]@61..62#0 , [alone] - LITER 42:Root[0000, 0]@63..73#0 StrRaw(2) raw - PUNCT 42:Root[0000, 0]@73..74#0 , [alone] - LITER 42:Root[0000, 0]@75..78#0 Char a - PUNCT 42:Root[0000, 0]@78..79#0 , [alone] - LITER 42:Root[0000, 0]@80..84#0 Byte b - PUNCT 42:Root[0000, 0]@84..85#0 , [alone] - LITER 42:Root[0000, 0]@86..93#0 CStr null - - - LITER 42:Root[0000, 0]@0..4#0 Integer 1u16 - PUNCT 42:Root[0000, 0]@4..5#0 , [alone] - LITER 42:Root[0000, 0]@6..11#0 Integer 2_u32 - PUNCT 42:Root[0000, 0]@11..12#0 , [alone] - PUNCT 42:Root[0000, 0]@13..14#0 - [alone] - LITER 42:Root[0000, 0]@14..18#0 Integer 4i64 - PUNCT 42:Root[0000, 0]@18..19#0 , [alone] - LITER 42:Root[0000, 0]@20..27#0 Float 3.14f32 - PUNCT 42:Root[0000, 0]@27..28#0 , [alone] - LITER 42:Root[0000, 0]@29..43#0 Str hello bridge - PUNCT 42:Root[0000, 0]@43..44#0 , [alone] - LITER 42:Root[0000, 0]@45..61#0 Str suffixedsuffix - PUNCT 42:Root[0000, 0]@61..62#0 , [alone] - LITER 42:Root[0000, 0]@63..73#0 StrRaw(2) raw - PUNCT 42:Root[0000, 0]@73..74#0 , [alone] - LITER 42:Root[0000, 0]@75..78#0 Char a - PUNCT 42:Root[0000, 0]@78..79#0 , [alone] - LITER 42:Root[0000, 0]@80..84#0 Byte b - PUNCT 42:Root[0000, 0]@84..85#0 , [alone] - LITER 42:Root[0000, 0]@86..93#0 CStr null + LITER 42:Root[0000, 0]@0..4#1 Integer 1u16 + PUNCT 42:Root[0000, 0]@4..5#1 , [alone] + LITER 42:Root[0000, 0]@6..11#1 Integer 2_u32 + PUNCT 42:Root[0000, 0]@11..12#1 , [alone] + PUNCT 42:Root[0000, 0]@13..14#1 - [alone] + LITER 42:Root[0000, 0]@14..18#1 Integer 4i64 + PUNCT 42:Root[0000, 0]@18..19#1 , [alone] + LITER 42:Root[0000, 0]@20..27#1 Float 3.14f32 + PUNCT 42:Root[0000, 0]@27..28#1 , [alone] + LITER 42:Root[0000, 0]@29..43#1 Str hello bridge + PUNCT 42:Root[0000, 0]@43..44#1 , [alone] + LITER 42:Root[0000, 0]@45..61#1 Str suffixedsuffix + PUNCT 42:Root[0000, 0]@61..62#1 , [alone] + LITER 42:Root[0000, 0]@63..73#1 StrRaw(2) raw + PUNCT 42:Root[0000, 0]@73..74#1 , [alone] + LITER 42:Root[0000, 0]@75..78#1 Char a + PUNCT 42:Root[0000, 0]@78..79#1 , [alone] + LITER 42:Root[0000, 0]@80..84#1 Byte b + PUNCT 42:Root[0000, 0]@84..85#1 , [alone] + LITER 42:Root[0000, 0]@86..93#1 CStr null + + + LITER 42:Root[0000, 0]@0..4#1 Integer 1u16 + PUNCT 42:Root[0000, 0]@4..5#1 , [alone] + LITER 42:Root[0000, 0]@6..11#1 Integer 2_u32 + PUNCT 42:Root[0000, 0]@11..12#1 , [alone] + PUNCT 42:Root[0000, 0]@13..14#1 - [alone] + LITER 42:Root[0000, 0]@14..18#1 Integer 4i64 + PUNCT 42:Root[0000, 0]@18..19#1 , [alone] + LITER 42:Root[0000, 0]@20..27#1 Float 3.14f32 + PUNCT 42:Root[0000, 0]@27..28#1 , [alone] + LITER 42:Root[0000, 0]@29..43#1 Str hello bridge + PUNCT 42:Root[0000, 0]@43..44#1 , [alone] + LITER 42:Root[0000, 0]@45..61#1 Str suffixedsuffix + PUNCT 42:Root[0000, 0]@61..62#1 , [alone] + LITER 42:Root[0000, 0]@63..73#1 StrRaw(2) raw + PUNCT 42:Root[0000, 0]@73..74#1 , [alone] + LITER 42:Root[0000, 0]@75..78#1 Char a + PUNCT 42:Root[0000, 0]@78..79#1 , [alone] + LITER 42:Root[0000, 0]@80..84#1 Byte b + PUNCT 42:Root[0000, 0]@84..85#1 , [alone] + LITER 42:Root[0000, 0]@86..93#1 CStr null "#]], ); } @@ -593,30 +593,30 @@ fn test_fn_like_macro_negative_literals() { LITER 1 Float 2.7 "#]], expect![[r#" - PUNCT 42:Root[0000, 0]@0..1#0 - [alone] - LITER 42:Root[0000, 0]@1..5#0 Integer 1u16 - PUNCT 42:Root[0000, 0]@5..6#0 , [alone] - PUNCT 42:Root[0000, 0]@7..8#0 - [alone] - LITER 42:Root[0000, 0]@9..14#0 Integer 2_u32 - PUNCT 42:Root[0000, 0]@14..15#0 , [alone] - PUNCT 42:Root[0000, 0]@16..17#0 - [alone] - LITER 42:Root[0000, 0]@17..24#0 Float 3.14f32 - PUNCT 42:Root[0000, 0]@24..25#0 , [alone] - PUNCT 42:Root[0000, 0]@26..27#0 - [alone] - LITER 42:Root[0000, 0]@28..31#0 Float 2.7 - - - PUNCT 42:Root[0000, 0]@0..1#0 - [alone] - LITER 42:Root[0000, 0]@1..5#0 Integer 1u16 - PUNCT 42:Root[0000, 0]@5..6#0 , [alone] - PUNCT 42:Root[0000, 0]@7..8#0 - [alone] - LITER 42:Root[0000, 0]@9..14#0 Integer 2_u32 - PUNCT 42:Root[0000, 0]@14..15#0 , [alone] - PUNCT 42:Root[0000, 0]@16..17#0 - [alone] - LITER 42:Root[0000, 0]@17..24#0 Float 3.14f32 - PUNCT 42:Root[0000, 0]@24..25#0 , [alone] - PUNCT 42:Root[0000, 0]@26..27#0 - [alone] - LITER 42:Root[0000, 0]@28..31#0 Float 2.7 + PUNCT 42:Root[0000, 0]@0..1#1 - [alone] + LITER 42:Root[0000, 0]@1..5#1 Integer 1u16 + PUNCT 42:Root[0000, 0]@5..6#1 , [alone] + PUNCT 42:Root[0000, 0]@7..8#1 - [alone] + LITER 42:Root[0000, 0]@9..14#1 Integer 2_u32 + PUNCT 42:Root[0000, 0]@14..15#1 , [alone] + PUNCT 42:Root[0000, 0]@16..17#1 - [alone] + LITER 42:Root[0000, 0]@17..24#1 Float 3.14f32 + PUNCT 42:Root[0000, 0]@24..25#1 , [alone] + PUNCT 42:Root[0000, 0]@26..27#1 - [alone] + LITER 42:Root[0000, 0]@28..31#1 Float 2.7 + + + PUNCT 42:Root[0000, 0]@0..1#1 - [alone] + LITER 42:Root[0000, 0]@1..5#1 Integer 1u16 + PUNCT 42:Root[0000, 0]@5..6#1 , [alone] + PUNCT 42:Root[0000, 0]@7..8#1 - [alone] + LITER 42:Root[0000, 0]@9..14#1 Integer 2_u32 + PUNCT 42:Root[0000, 0]@14..15#1 , [alone] + PUNCT 42:Root[0000, 0]@16..17#1 - [alone] + LITER 42:Root[0000, 0]@17..24#1 Float 3.14f32 + PUNCT 42:Root[0000, 0]@24..25#1 , [alone] + PUNCT 42:Root[0000, 0]@26..27#1 - [alone] + LITER 42:Root[0000, 0]@28..31#1 Float 2.7 "#]], ); } @@ -647,20 +647,20 @@ fn test_attr_macro() { PUNCT 1 ; [alone] "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..3#0 mod - IDENT 42:Root[0000, 0]@4..5#0 m - GROUP {} 42:Root[0000, 0]@6..7#0 42:Root[0000, 0]@7..8#0 42:Root[0000, 0]@6..8#0 + IDENT 42:Root[0000, 0]@0..3#1 mod + IDENT 42:Root[0000, 0]@4..5#1 m + GROUP {} 42:Root[0000, 0]@6..7#1 42:Root[0000, 0]@7..8#1 42:Root[0000, 0]@6..8#1 - IDENT 42:Root[0000, 0]@0..4#0 some - IDENT 42:Root[0000, 0]@5..14#0 arguments + IDENT 42:Root[0000, 0]@0..4#1 some + IDENT 42:Root[0000, 0]@5..14#1 arguments - IDENT 42:Root[0000, 0]@0..13#0 compile_error - PUNCT 42:Root[0000, 0]@13..14#0 ! [joint] - GROUP () 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@55..56#0 42:Root[0000, 0]@14..56#0 - LITER 42:Root[0000, 0]@15..55#0 Str #[attr_error(some arguments)] mod m {} - PUNCT 42:Root[0000, 0]@56..57#0 ; [alone] + IDENT 42:Root[0000, 0]@0..13#1 compile_error + PUNCT 42:Root[0000, 0]@13..14#1 ! [joint] + GROUP () 42:Root[0000, 0]@14..15#1 42:Root[0000, 0]@55..56#1 42:Root[0000, 0]@14..56#1 + LITER 42:Root[0000, 0]@15..55#1 Str #[attr_error(some arguments)] mod m {} + PUNCT 42:Root[0000, 0]@56..57#1 ; [alone] "#]], ); } @@ -722,8 +722,8 @@ fn test_fn_like_span_line_column() { " hello", expect![[r#" - LITER 42:Root[0000, 0]@0..100#0 Integer 2 - LITER 42:Root[0000, 0]@0..100#0 Integer 1 + LITER 42:Root[0000, 0]@0..100#1 Integer 2 + LITER 42:Root[0000, 0]@0..100#1 Integer 1 "#]], ); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs index 8a1d5f336dd78..a5377b8d1b2da 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs @@ -317,7 +317,7 @@ impl flags::Lsif { VendoredLibrariesConfig::Included { workspace_root: &path.clone().into() } }; - let si = StaticIndex::compute(&analysis, vendored_libs_config); + let si = StaticIndex::compute(&analysis, vendored_libs_config, true); let mut lsif = LsifManager::new(&analysis, db, &vfs, out); lsif.add_vertex(lsif::Vertex::MetaData(lsif::MetaData { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index 3d904e7148e2c..03d79915bc0a2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -72,7 +72,7 @@ impl flags::Scip { VendoredLibrariesConfig::Included { workspace_root: &root.clone().into() } }; - let si = StaticIndex::compute(&analysis, vendored_libs_config); + let si = StaticIndex::compute(&analysis, vendored_libs_config, false); let metadata = scip_types::Metadata { version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(), @@ -544,6 +544,7 @@ mod test { VendoredLibrariesConfig::Included { workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()), }, + false, ); let FilePosition { file_id, offset } = position; @@ -912,6 +913,7 @@ pub mod example_mod { VendoredLibrariesConfig::Included { workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()), }, + false, ); let file = si.files.first().unwrap(); @@ -935,6 +937,7 @@ pub mod example_mod { VendoredLibrariesConfig::Included { workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()), }, + false, ); let file = si.files.first().unwrap(); @@ -963,6 +966,7 @@ pub mod example_mod { VendoredLibrariesConfig::Included { workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()), }, + false, ); let file = si.files.first().unwrap(); @@ -995,6 +999,7 @@ pub mod example_mod { VendoredLibrariesConfig::Included { workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()), }, + false, ); let file = si.files.first().unwrap(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs index d9a56098bd9dc..f311fe9da2d42 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs @@ -59,33 +59,35 @@ impl flags::UnresolvedReferences { !source_root.is_library }); - for module in work { - let file_id = module.definition_source_file_id(db).original_file(db); - let file_id = file_id.file_id(db); - if !visited_files.contains(&file_id) { - let crate_name = module - .krate(db) - .display_name(db) - .as_deref() - .unwrap_or(&sym::unknown) - .to_owned(); - let file_path = vfs.file_path(file_id); - eprintln!("processing crate: {crate_name}, module: {file_path}",); - - let line_index = line_index(db, file_id); - let file_text = db.file_text(file_id); - - for range in find_unresolved_references(db, &sema, file_id, &module) { - let line_col = line_index.line_col(range.start()); - let line = line_col.line + 1; - let col = line_col.col + 1; - let text = &file_text.text(db)[range]; - println!("{file_path}:{line}:{col}: {text}"); + hir::attach_db(db, || { + for module in work { + let file_id = module.definition_source_file_id(db).original_file(db); + let file_id = file_id.file_id(db); + if !visited_files.contains(&file_id) { + let crate_name = module + .krate(db) + .display_name(db) + .as_deref() + .unwrap_or(&sym::unknown) + .to_owned(); + let file_path = vfs.file_path(file_id); + eprintln!("processing crate: {crate_name}, module: {file_path}",); + + let line_index = line_index(db, file_id); + let file_text = db.file_text(file_id); + + for range in find_unresolved_references(db, &sema, file_id, &module) { + let line_col = line_index.line_col(range.start()); + let line = line_col.line + 1; + let col = line_col.col + 1; + let text = &file_text.text(db)[range]; + println!("{file_path}:{line}:{col}: {text}"); + } + + visited_files.insert(file_id); } - - visited_files.insert(file_id); } - } + }); eprintln!(); eprintln!("scan complete"); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 1e7c8257d15b4..0258ec5ce33bd 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -712,6 +712,9 @@ config_data! { /// Term search fuel in "units of work" for autocompletion (Defaults to 1000). completion_termSearch_fuel: usize = 1000, + /// Complete turbofish (`::<>`) at the call site (if needed). + completion_turbofish_enable: bool = true, + /// List of rust-analyzer diagnostics to disable. diagnostics_disabled: FxHashSet = FxHashSet::default(), @@ -1928,6 +1931,7 @@ impl Config { limit: self.completion_limit(source_root).to_owned(), enable_term_search: self.completion_termSearch_enable(source_root).to_owned(), term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64, + enable_turbofish: self.completion_turbofish_enable(source_root).to_owned(), fields_to_resolve: if self.client_is_neovim() { CompletionFieldsToResolve::empty() } else { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index 93fa8725e777b..9562001b9d95d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -320,6 +320,7 @@ fn completion_config() -> CompletionConfig<'static> { enable_self_on_the_fly: true, enable_private_editable: true, enable_term_search: true, + enable_turbofish: true, term_search_fuel: 200, full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 66e7031c356e7..a9759ebed0cf9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -546,7 +546,6 @@ impl GlobalState { if client_refresh { // Refresh semantic tokens if the client supports it. if self.config.semantic_tokens_refresh() { - self.semantic_tokens_cache.lock().clear(); self.send_request::((), |_, _| ()); } @@ -921,7 +920,10 @@ impl GlobalState { } Err(e) => self.show_message( lsp_types::MessageType::Error, - format!("Failed to spawn project discovery command: {e:#}"), + format!( + "Failed to spawn project discovery command in {}: {e:#}", + current_dir, + ), false, ), } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 4199cb29e66d2..8a3ea34394a31 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -24,13 +24,16 @@ use std::{path::PathBuf, time::Instant}; use ide_db::FxHashMap; use lsp_types::{ CodeActionContext, CodeActionParams, CodeActionRequest, CompletionParams, CompletionRequest, + DidChangeTextDocumentNotification, DidChangeTextDocumentParams, DidOpenTextDocumentNotification, DidOpenTextDocumentParams, DocumentFormattingParams, DocumentFormattingRequest, DocumentRangeFormattingParams, DocumentRangeFormattingRequest, FileRename, FormattingOptions, HoverParams, HoverRequest, InlayHint, InlayHintParams, InlayHintRequest, InlayHintResolveRequest, Label, LanguageKind, PartialResultParams, Position, - Range, RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, TypeDefinitionParams, - TypeDefinitionRequest, Uri, WillRenameFilesRequest, WorkDoneProgressParams, - WorkspaceSymbolRequest, + Range, RenameFilesParams, SemanticTokensDeltaParams, SemanticTokensDeltaRequest, + SemanticTokensParams, SemanticTokensRequest, TextDocumentContentChangeEvent, + TextDocumentContentChangeWholeDocument, TextDocumentItem, TextDocumentPositionParams, + TypeDefinitionParams, TypeDefinitionRequest, Uri, VersionedTextDocumentIdentifier, + WillRenameFilesRequest, WorkDoneProgressParams, WorkspaceSymbolRequest, }; use rust_analyzer::lsp::ext::{OnEnterRequest, RunnablesParams, RunnablesRequest}; use serde_json::json; @@ -984,6 +987,70 @@ version = \"0.0.0\" ); } +#[test] +fn semantic_tokens_delta_after_edit() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- /Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- /src/lib.rs +fn foo() {} +"#, + ) + .server() + .wait_until_workspace_is_loaded(); + + let doc = server.doc_id("src/lib.rs"); + server.notification::(DidOpenTextDocumentParams { + text_document: TextDocumentItem { + uri: doc.uri.clone(), + language_id: LanguageKind::Rust, + version: 0, + text: "fn foo() {}\n".to_owned(), + }, + }); + + let full = server.send_request::(SemanticTokensParams { + text_document: doc.clone(), + work_done_progress_params: WorkDoneProgressParams::default(), + partial_result_params: PartialResultParams::default(), + }); + let previous_result_id = full["resultId"].as_str().unwrap().to_owned(); + + server.notification::(DidChangeTextDocumentParams { + text_document: VersionedTextDocumentIdentifier { + text_document_identifier: doc.clone(), + version: 1, + }, + content_changes: vec![ + TextDocumentContentChangeEvent::TextDocumentContentChangeWholeDocument( + TextDocumentContentChangeWholeDocument { + text: "fn foo() {}\nfn bar() {}\n".to_owned(), + }, + ), + ], + }); + server.wait_for_semantic_tokens_refresh(); + + let res = server.send_request::(SemanticTokensDeltaParams { + text_document: doc, + previous_result_id, + work_done_progress_params: WorkDoneProgressParams::default(), + partial_result_params: PartialResultParams::default(), + }); + assert!( + res.get("edits").is_some() && res.get("data").is_none(), + "expected a delta response, got {res}" + ); +} + fn out_dirs_check_impl(root_contains_symlink: bool) { let mut server = Project::with_fixture( r###" @@ -1254,6 +1321,7 @@ pub fn foo(_input: TokenStream) -> TokenStream { ``` ```rust + impl Bar for Foo fn bar() ```"#]] .assert_eq(value); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index 2a469a5bfb80f..ecca26f57f37f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -226,6 +226,9 @@ impl Project<'_> { ]), ..Default::default() }), + semantic_tokens: Some(lsp_types::SemanticTokensWorkspaceClientCapabilities { + refresh_support: Some(true), + }), ..Default::default() }), text_document: Some(lsp_types::TextDocumentClientCapabilities { @@ -378,7 +381,10 @@ impl Server { { continue; } - } else if req.method != "workspace/diagnostic/refresh" { + } else if !matches!( + req.method.as_str(), + "workspace/diagnostic/refresh" | "workspace/semanticTokens/refresh" + ) { panic!("unexpected request: {req:?}") } } @@ -458,6 +464,22 @@ impl Server { } } + pub(crate) fn wait_for_semantic_tokens_refresh(&self) { + loop { + let msg = self + .recv() + .unwrap_or_else(|Timeout| { + panic!("timeout while waiting for semantic tokens refresh") + }) + .expect("connection closed while waiting for semantic tokens refresh"); + if let Message::Request(req) = &msg + && req.method == "workspace/semanticTokens/refresh" + { + return; + } + } + } + fn wait_for_message_cond( &self, n: usize, diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 64ce120ad0fd3..d7e7013f1dab5 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -19,223 +19,122 @@ //! # The Call-site Hierarchy //! //! `ExpnData::call_site` in rustc, `MacroCallLoc::call_site` in rust-analyzer. -#[cfg(feature = "salsa")] -use crate::Edition; -use std::fmt; +pub use self::imp::*; /// A syntax context describes a hierarchy tracking order of macro definitions. #[cfg(feature = "salsa")] -#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] -pub struct SyntaxContext( - /// # Invariant - /// - /// This is either a valid `salsa::Id` or a root `SyntaxContext`. - u32, - std::marker::PhantomData<&'static salsa::plumbing::interned::Value>, -); +mod imp { + use std::{fmt, num::NonZeroU32}; -#[cfg(feature = "salsa")] -const _: () = { - use crate::MacroCallId; - use salsa::plumbing as zalsa_; - use salsa::plumbing::interned as zalsa_struct_; - - #[derive(Clone, Eq, Debug)] - pub struct SyntaxContextData { - outer_expn: Option, + use salsa::{ + Database, + plumbing::{AsId, FromId}, + }; + use syntax::Edition; + + use crate::{MacroCallId, Transparency}; + + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + struct SyntaxContextData { + outer_expn: MacroCallId, outer_transparency: Transparency, edition: Edition, parent: SyntaxContext, - opaque: SyntaxContext, - opaque_and_semiopaque: SyntaxContext, + opaque_if_not_self: Option, + opaque_and_semiopaque_if_not_self: Option, } - impl PartialEq for SyntaxContextData { - fn eq(&self, other: &Self) -> bool { - self.outer_expn == other.outer_expn - && self.outer_transparency == other.outer_transparency - && self.edition == other.edition - && self.parent == other.parent + impl SyntaxContextData { + #[inline] + fn opaque(&self, ctx: SyntaxContext) -> SyntaxContext { + self.opaque_if_not_self.unwrap_or(ctx) } - } - impl std::hash::Hash for SyntaxContextData { - fn hash(&self, state: &mut H) { - self.outer_expn.hash(state); - self.outer_transparency.hash(state); - self.edition.hash(state); - self.parent.hash(state); + #[inline] + fn opaque_and_semiopaque(&self, ctx: SyntaxContext) -> SyntaxContext { + self.opaque_and_semiopaque_if_not_self.unwrap_or(ctx) } } - impl zalsa_::HasJar for SyntaxContext { - type Jar = zalsa_struct_::JarImpl; - const KIND: zalsa_::JarKind = zalsa_::JarKind::Struct; + #[salsa::interned(unsafe(no_lifetime), revisions = usize::MAX)] + struct SyntaxContextImpl { + data: SyntaxContextData, } - zalsa_::register_jar! { - zalsa_::ErasedJar::erase::() - } - - /// Key to use during hash lookups. Each field is some type that implements `Lookup` - /// for the owned type. This permits interning with an `&str` when a `String` is required and so forth. - #[derive(Hash)] - struct StructKey<'db, T0, T1, T2, T3>(T0, T1, T2, T3, std::marker::PhantomData<&'db ()>); - - impl<'db, T0, T1, T2, T3> zalsa_::HashEqLike> for SyntaxContextData - where - Option: zalsa_::HashEqLike, - Transparency: zalsa_::HashEqLike, - Edition: zalsa_::HashEqLike, - SyntaxContext: zalsa_::HashEqLike, - { - fn hash(&self, h: &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_::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) - } - } - - // SAFETY: `Self::Fields`, i.e. `SyntaxContextData`, doesn't contain any lifetimes. - unsafe impl zalsa_struct_::Configuration for SyntaxContext { - const LOCATION: salsa::plumbing::Location = - salsa::plumbing::Location { file: file!(), line: line!() }; - const DEBUG_NAME: &'static str = "SyntaxContextData"; - const REVISIONS: std::num::NonZeroUsize = std::num::NonZeroUsize::MAX; - const PERSIST: bool = false; - - type Fields<'a> = SyntaxContextData; - type Struct<'a> = SyntaxContext; - - fn serialize(_: &Self::Fields<'_>, _: S) -> Result - where - S: zalsa_::serde::Serializer, - { - unimplemented!("attempted to serialize value that set `PERSIST` to false") - } - - fn deserialize<'de, D>(_: D) -> Result, D::Error> - where - D: zalsa_::serde::Deserializer<'de>, - { - unimplemented!("attempted to deserialize value that cannot set `PERSIST` to false"); - } - } + #[derive(Clone, Copy, PartialEq, Eq, Hash)] + pub struct SyntaxContext(NonZeroU32); impl SyntaxContext { - pub fn ingredient(zalsa: &zalsa_::Zalsa) -> &zalsa_struct_::IngredientImpl { - static CACHE: zalsa_::IngredientCache> = - zalsa_::IngredientCache::new(); + const MAX_ROOT_ID: u32 = salsa::Id::MAX_U32 + 1 + Edition::LATEST as u32; - // SAFETY: The ingredient at offset 0 in `JarImpl` has type - // `IngredientImpl`. - unsafe { CACHE.get_or_create::, 0>(zalsa) } - } - } - impl zalsa_::AsId for SyntaxContext { - fn as_id(&self) -> salsa::Id { - self.as_salsa_id().expect("`SyntaxContext::as_id()` called on a root `SyntaxContext`") - } - } - impl zalsa_::FromId for SyntaxContext { - fn from_id(id: salsa::Id) -> Self { - Self::from_salsa_id(id) + #[inline] + pub fn into_u32(self) -> u32 { + self.0.get() - 1 } - } - unsafe impl Send for SyntaxContext {} - - unsafe impl Sync for SyntaxContext {} - impl zalsa_::SalsaStructInDb for SyntaxContext { - type MemoIngredientMap = salsa::plumbing::MemoIngredientSingletonIndex; - const LEAF_TYPE_IDS: &[salsa::plumbing::ConstTypeId] = - &[salsa::plumbing::ConstTypeId::of::()]; + /// # Safety + /// + /// The ID must be a valid `SyntaxContext`. + #[inline] + pub unsafe fn from_u32(u32: u32) -> Self { + // INVARIANT: Our precondition. + Self(NonZeroU32::new(u32 + 1).unwrap_or_else(|| panic!("invalid SyntaxContext({u32})"))) + } - fn lookup_ingredient_index(aux: &zalsa_::Zalsa) -> salsa::plumbing::IngredientIndices { - aux.lookup_jar_by_type::>().into() + #[inline] + fn id(self) -> Option { + if self.is_root() { + None + } else { + // SAFETY: By our invariant, this is either a root (which we verified it's not) or a + // valid `salsa::Id` index. + unsafe { Some(SyntaxContextImpl::from_id(salsa::Id::from_index(self.0.get() - 1))) } + } } - fn entries(zalsa: &zalsa_::Zalsa) -> impl Iterator + '_ { - let _ingredient_index = - zalsa.lookup_jar_by_type::>(); - ::ingredient(zalsa).entries(zalsa).map(|entry| entry.key()) + #[inline] + pub fn is_root(self) -> bool { + (SyntaxContext::MAX_ROOT_ID - Edition::LATEST as u32) <= self.0.get() + && self.0.get() <= (SyntaxContext::MAX_ROOT_ID - Edition::Edition2015 as u32) } #[inline] - fn cast(id: salsa::Id, type_id: std::any::TypeId) -> Option { - if type_id == std::any::TypeId::of::() { - Some(::from_id(id)) - } else { - None + pub fn remove_root_edition(&mut self) { + if self.is_root() { + *self = Self::root(Edition::Edition2015); } } + /// The root context, which is the parent of all other contexts. All `FileId`s have this context. #[inline] - unsafe fn memo_table( - zalsa: &zalsa_::Zalsa, - id: zalsa_::Id, - current_revision: zalsa_::Revision, - ) -> zalsa_::MemoTableWithTypes<'_> { - // SAFETY: Guaranteed by caller. - unsafe { - zalsa.table().memos::>(id, current_revision) - } + pub const fn root(edition: Edition) -> Self { + let edition = edition as u32; + // INVARIANT: Roots are valid `SyntaxContext`s + SyntaxContext(NonZeroU32::new(SyntaxContext::MAX_ROOT_ID - edition).unwrap()) } - } - unsafe impl salsa::plumbing::SalsaValue for SyntaxContext {} - impl<'db> SyntaxContext { - pub fn new< - Db, - 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, - outer_transparency: T1, - edition: T2, - parent: T3, - opaque: impl FnOnce(SyntaxContext) -> SyntaxContext, - opaque_and_semiopaque: impl FnOnce(SyntaxContext) -> SyntaxContext, - ) -> Self - where - Db: ?Sized + salsa::Database, - Option: zalsa_::HashEqLike, - Transparency: zalsa_::HashEqLike, - Edition: zalsa_::HashEqLike, - SyntaxContext: zalsa_::HashEqLike, - { - let (zalsa, zalsa_local) = db.zalsas(); - - SyntaxContext::ingredient(zalsa).intern( - zalsa, - zalsa_local, - StructKey::<'db>( + pub fn new( + db: &dyn Database, + outer_expn: MacroCallId, + outer_transparency: Transparency, + edition: Edition, + parent: SyntaxContext, + opaque_if_not_self: Option, + opaque_and_semiopaque_if_not_self: Option, + ) -> Self { + let result = SyntaxContextImpl::new( + db, + SyntaxContextData { outer_expn, outer_transparency, edition, parent, - std::marker::PhantomData, - ), - |id, data| SyntaxContextData { - 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)), + opaque_if_not_self, + opaque_and_semiopaque_if_not_self, }, - ) + ); + SyntaxContext(NonZeroU32::new(result.as_id().index() + 1).unwrap()) } /// Invariant: Only the root [`SyntaxContext`] has a [`None`] outer expansion. @@ -243,211 +142,183 @@ const _: () = { // MacroCallId is reserved anyways so we can do bit tagging here just fine. // The bigger issue is that this will cause interning to now create completely separate chains // per crate. Though that is likely not a problem as `MacroCallId`s are already crate calling dependent. - pub fn outer_expn(self, db: &'db Db) -> Option - where - Db: ?Sized + zalsa_::Database, - { - let id = self.as_salsa_id()?; - let zalsa = db.zalsa(); - let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); - fields.outer_expn - } - - pub fn outer_transparency(self, db: &'db Db) -> Transparency - where - Db: ?Sized + zalsa_::Database, - { - let Some(id) = self.as_salsa_id() else { return Transparency::Opaque }; - let zalsa = db.zalsa(); - let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); - fields.outer_transparency - } - - pub fn edition(self, db: &'db Db) -> Edition - where - Db: ?Sized + zalsa_::Database, - { - match self.as_salsa_id() { - Some(id) => { - let zalsa = db.zalsa(); - let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); - fields.edition - } - None => Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.into_u32()), + #[inline] + pub fn outer_expn(self, db: &dyn Database) -> Option { + self.id().map(|id| id.data(db).outer_expn) + } + + #[inline] + pub fn outer_transparency(self, db: &dyn Database) -> Transparency { + match self.id() { + Some(id) => id.data(db).outer_transparency, + None => Transparency::Opaque, } } - pub fn parent(self, db: &'db Db) -> SyntaxContext - where - Db: ?Sized + zalsa_::Database, - { - match self.as_salsa_id() { - Some(id) => { - let zalsa = db.zalsa(); - let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); - fields.parent - } + #[inline] + pub fn edition(self, db: &dyn Database) -> Edition { + match self.id() { + Some(id) => id.data(db).edition, + None => Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.0.get()), + } + } + + #[inline] + pub fn parent(self, db: &dyn Database) -> SyntaxContext { + match self.id() { + Some(id) => id.data(db).parent, None => self, } } /// This context, but with all transparent and semi-opaque expansions filtered away. - pub fn opaque(self, db: &'db Db) -> SyntaxContext - where - Db: ?Sized + zalsa_::Database, - { - match self.as_salsa_id() { - Some(id) => { - let zalsa = db.zalsa(); - let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); - fields.opaque - } + #[inline] + pub fn opaque(self, db: &dyn Database) -> SyntaxContext { + match self.id() { + Some(id) => id.data(db).opaque(self), None => self, } } /// This context, but with all transparent expansions filtered away. - pub fn opaque_and_semiopaque(self, db: &'db Db) -> SyntaxContext - where - Db: ?Sized + zalsa_::Database, - { - match self.as_salsa_id() { - Some(id) => { - let zalsa = db.zalsa(); - let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); - fields.opaque_and_semiopaque - } + #[inline] + pub fn opaque_and_semiopaque(self, db: &dyn Database) -> SyntaxContext { + match self.id() { + Some(id) => id.data(db).opaque_and_semiopaque(self), None => self, } } - } -}; - -#[cfg(feature = "salsa")] -impl<'db> SyntaxContext { - const MAX_ROOT_ID: u32 = salsa::Id::MAX_U32 + Edition::LATEST as u32; - #[inline] - pub const fn into_u32(self) -> u32 { - self.0 - } + #[inline] + pub fn opaque_and_opaque_and_semiopaque( + self, + db: &dyn Database, + ) -> (SyntaxContext, SyntaxContext) { + match self.id() { + Some(id) => { + let data = id.data(db); + (data.opaque(self), data.opaque_and_semiopaque(self)) + } + None => (self, self), + } + } - /// # Safety - /// - /// The ID must be a valid `SyntaxContext`. - #[inline] - pub const unsafe fn from_u32(u32: u32) -> Self { - // INVARIANT: Our precondition. - Self(u32, std::marker::PhantomData) - } + #[inline] + pub fn outer_mark(self, db: &dyn Database) -> (Option, Transparency) { + match self.id() { + Some(id) => { + let data = id.data(db); + (Some(data.outer_expn), data.outer_transparency) + } + None => (None, Transparency::Opaque), + } + } - #[inline] - fn as_salsa_id(self) -> Option { - if self.is_root() { - None - } else { - // SAFETY: By our invariant, this is either a root (which we verified it's not) or a - // valid `salsa::Id` index. - unsafe { Some(salsa::Id::from_index(self.0)) } + #[inline] + pub fn normalize_to_macros_2_0(self, db: &dyn Database) -> SyntaxContext { + self.opaque(db) } - } - #[inline] - fn from_salsa_id(id: salsa::Id) -> Self { - // SAFETY: This comes from a Salsa ID. - unsafe { Self::from_u32(id.index()) } - } + #[inline] + pub fn normalize_to_macro_rules(self, db: &dyn Database) -> SyntaxContext { + self.opaque_and_semiopaque(db) + } - #[inline] - pub fn is_root(self) -> bool { - (SyntaxContext::MAX_ROOT_ID - Edition::LATEST as u32) <= self.into_u32() - && self.into_u32() <= (SyntaxContext::MAX_ROOT_ID - Edition::Edition2015 as u32) - } + #[inline] + pub fn is_opaque(self, db: &dyn Database) -> bool { + !self.is_root() && self.outer_transparency(db).is_opaque() + } - #[inline] - pub fn remove_root_edition(&mut self) { - if self.is_root() { - *self = Self::root(Edition::Edition2015); + #[inline] + pub fn remove_mark(&mut self, db: &dyn Database) -> (Option, Transparency) { + match self.id() { + Some(id) => { + let data = id.data(db); + *self = data.parent; + (Some(data.outer_expn), data.outer_transparency) + } + None => (None, Transparency::Opaque), + } } - } - /// The root context, which is the parent of all other contexts. All `FileId`s have this context. - #[inline] - pub const fn root(edition: Edition) -> Self { - let edition = edition as u32; - // SAFETY: Roots are valid `SyntaxContext`s - unsafe { SyntaxContext::from_u32(SyntaxContext::MAX_ROOT_ID - edition) } - } + pub fn marks(self, db: &dyn Database) -> impl Iterator { + let mut marks = self.marks_rev(db).collect::>(); + marks.reverse(); + marks.into_iter() + } - #[inline] - pub fn outer_mark( - self, - db: &'db dyn salsa::Database, - ) -> (Option, Transparency) { - (self.outer_expn(db), self.outer_transparency(db)) + #[inline] + pub fn marks_rev( + self, + db: &dyn Database, + ) -> impl Iterator { + let data = move |ctx: SyntaxContext| ctx.id().map(|it| it.data(db)); + std::iter::successors(data(self), move |ctx| data(ctx.parent)) + .map(|ctx| (ctx.outer_expn, ctx.outer_transparency)) + } } - #[inline] - pub fn normalize_to_macros_2_0(self, db: &'db dyn salsa::Database) -> SyntaxContext { - self.opaque(db) + impl fmt::Display for SyntaxContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.is_root() { + write!( + f, + "ROOT{}", + Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.0.get()).number() + ) + } else { + write!(f, "{}", self.0.get()) + } + } } - #[inline] - pub fn normalize_to_macro_rules(self, db: &'db dyn salsa::Database) -> SyntaxContext { - self.opaque_and_semiopaque(db) + impl fmt::Debug for SyntaxContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if f.alternate() { + fmt::Display::fmt(self, f) + } else { + f.debug_tuple("SyntaxContext").field(&self.0).finish() + } + } } +} - pub fn is_opaque(self, db: &'db dyn salsa::Database) -> bool { - !self.is_root() && self.outer_transparency(db).is_opaque() - } +#[cfg(not(feature = "salsa"))] +mod imp { + use std::{fmt, num::NonZeroU32}; - pub fn remove_mark( - &mut self, - db: &'db dyn salsa::Database, - ) -> (Option, Transparency) { - let data = *self; - *self = data.parent(db); - (data.outer_expn(db), data.outer_transparency(db)) - } + #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] + pub struct SyntaxContext(NonZeroU32); - pub fn marks( - self, - db: &'db dyn salsa::Database, - ) -> impl Iterator { - let mut marks = self.marks_rev(db).collect::>(); - marks.reverse(); - marks.into_iter() - } + impl SyntaxContext { + #[inline] + pub fn into_u32(self) -> u32 { + self.0.get() - 1 + } - pub fn marks_rev( - self, - db: &'db dyn salsa::Database, - ) -> impl Iterator { - std::iter::successors(Some(self), move |&mark| Some(mark.parent(db))) - .take_while(|&it| !it.is_root()) - .map(|ctx| { - let mark = ctx.outer_mark(db); - // We stop before taking the root expansion, as such we cannot encounter a `None` outer - // expansion, as only the ROOT has it. - (mark.0.unwrap(), mark.1) - }) + /// # Safety + /// + /// None. This is always safe to call without the `salsa` feature. + #[inline] + pub unsafe fn from_u32(u32: u32) -> Self { + Self(NonZeroU32::new(u32 + 1).unwrap_or_else(|| panic!("invalid SyntaxContext({u32})"))) + } } -} -#[cfg(not(feature = "salsa"))] -#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] -pub struct SyntaxContext(u32); -#[cfg(not(feature = "salsa"))] -impl SyntaxContext { - pub const fn into_u32(self) -> u32 { - self.0 + impl fmt::Display for SyntaxContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0.get()) + } } - /// # Safety - /// - /// None. This is always safe to call without the `salsa` feature. - pub const unsafe fn from_u32(u32: u32) -> Self { - Self(u32) + impl fmt::Debug for SyntaxContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if f.alternate() { + fmt::Display::fmt(self, f) + } else { + f.debug_tuple("SyntaxContext").field(&self.0).finish() + } + } } } @@ -479,40 +350,10 @@ impl Transparency { } } -#[cfg(feature = "salsa")] -impl fmt::Display for SyntaxContext { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.is_root() { - write!( - f, - "ROOT{}", - Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.into_u32()).number() - ) - } else { - write!(f, "{}", self.into_u32()) - } - } -} - -#[cfg(not(feature = "salsa"))] -impl fmt::Display for SyntaxContext { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.into_u32()) - } -} - -impl std::fmt::Debug for SyntaxContext { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if f.alternate() { - fmt::Display::fmt(self, f) - } else { - f.debug_tuple("SyntaxContext").field(&self.0).finish() - } - } -} - #[cfg(test)] mod tests { + use syntax::Edition; + use super::*; #[test] @@ -562,12 +403,13 @@ mod tests { let root = SyntaxContext::root(Edition::Edition2015); let ctx = SyntaxContext::new( &db, - None, + // SAFETY: We never use this ID. + crate::MacroCallId(unsafe { salsa::Id::from_index(1) }), Transparency::Opaque, Edition::Edition2021, root, - |_| root, - |_| root, + Some(root), + Some(root), ); // SAFETY: The value was produced by `SyntaxContext::into_u32` above. diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 91387041a1116..5e421b7b91489 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -449,12 +449,16 @@ Expr = | LetExpr | UnderscoreExpr | IncludeBytesExpr +| CfgPredExpr // We need a special expression for this because we don't have access to the file, // and emitting a specific array will have problems with the length. IncludeBytesExpr = 'builtin' '#' 'include_bytes' +CfgPredExpr = + 'builtin' '#' 'cfg' '(' CfgPredicate ','? ')' + OffsetOfExpr = AnyAttr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')' @@ -763,6 +767,7 @@ Pat = | ConstBlockPat | DerefPat | NotNull +| CfgPredPat NotNull = '!' 'null' @@ -770,6 +775,9 @@ NotNull = DerefPat = 'builtin' '#' 'deref' '(' Pat ')' +CfgPredPat = + 'builtin' '#' 'cfg' '(' CfgPredicate ','? ')' + LiteralPat = '-'? Literal 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 ad52d82787278..2aca544155b93 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 @@ -442,6 +442,44 @@ impl CfgMeta { #[inline] pub fn cfg_token(&self) -> Option { support::token(&self.syntax, T![cfg]) } } +pub struct CfgPredExpr { + pub(crate) syntax: SyntaxNode, +} +impl CfgPredExpr { + #[inline] + pub fn cfg_predicate(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + #[inline] + pub fn comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } + #[inline] + pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } + #[inline] + pub fn cfg_token(&self) -> Option { support::token(&self.syntax, T![cfg]) } +} +pub struct CfgPredPat { + pub(crate) syntax: SyntaxNode, +} +impl CfgPredPat { + #[inline] + pub fn cfg_predicate(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + #[inline] + pub fn comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } + #[inline] + pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } + #[inline] + pub fn cfg_token(&self) -> Option { support::token(&self.syntax, T![cfg]) } +} pub struct ClosureExpr { pub(crate) syntax: SyntaxNode, } @@ -2216,6 +2254,7 @@ pub enum Expr { BreakExpr(BreakExpr), CallExpr(CallExpr), CastExpr(CastExpr), + CfgPredExpr(CfgPredExpr), ClosureExpr(ClosureExpr), ContinueExpr(ContinueExpr), FieldExpr(FieldExpr), @@ -2312,6 +2351,7 @@ pub enum Meta { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Pat { BoxPat(BoxPat), + CfgPredPat(CfgPredPat), ConstBlockPat(ConstBlockPat), DerefPat(DerefPat), IdentPat(IdentPat), @@ -3445,6 +3485,70 @@ impl fmt::Debug for CfgMeta { f.debug_struct("CfgMeta").field("syntax", &self.syntax).finish() } } +impl AstNode for CfgPredExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CFG_PRED_EXPR + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_PRED_EXPR } + #[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 CfgPredExpr { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for CfgPredExpr {} +impl PartialEq for CfgPredExpr { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for CfgPredExpr { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for CfgPredExpr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CfgPredExpr").field("syntax", &self.syntax).finish() + } +} +impl AstNode for CfgPredPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CFG_PRED_PAT + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_PRED_PAT } + #[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 CfgPredPat { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for CfgPredPat {} +impl PartialEq for CfgPredPat { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for CfgPredPat { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for CfgPredPat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CfgPredPat").field("syntax", &self.syntax).finish() + } +} impl AstNode for ClosureExpr { #[inline] fn kind() -> SyntaxKind @@ -8137,6 +8241,10 @@ impl From for Expr { #[inline] fn from(node: CastExpr) -> Expr { Expr::CastExpr(node) } } +impl From for Expr { + #[inline] + fn from(node: CfgPredExpr) -> Expr { Expr::CfgPredExpr(node) } +} impl From for Expr { #[inline] fn from(node: ClosureExpr) -> Expr { Expr::ClosureExpr(node) } @@ -8263,6 +8371,7 @@ impl AstNode for Expr { | BREAK_EXPR | CALL_EXPR | CAST_EXPR + | CFG_PRED_EXPR | CLOSURE_EXPR | CONTINUE_EXPR | FIELD_EXPR @@ -8305,6 +8414,7 @@ impl AstNode for Expr { BREAK_EXPR => Expr::BreakExpr(BreakExpr { syntax }), CALL_EXPR => Expr::CallExpr(CallExpr { syntax }), CAST_EXPR => Expr::CastExpr(CastExpr { syntax }), + CFG_PRED_EXPR => Expr::CfgPredExpr(CfgPredExpr { syntax }), CLOSURE_EXPR => Expr::ClosureExpr(ClosureExpr { syntax }), CONTINUE_EXPR => Expr::ContinueExpr(ContinueExpr { syntax }), FIELD_EXPR => Expr::FieldExpr(FieldExpr { syntax }), @@ -8349,6 +8459,7 @@ impl AstNode for Expr { Expr::BreakExpr(it) => &it.syntax, Expr::CallExpr(it) => &it.syntax, Expr::CastExpr(it) => &it.syntax, + Expr::CfgPredExpr(it) => &it.syntax, Expr::ClosureExpr(it) => &it.syntax, Expr::ContinueExpr(it) => &it.syntax, Expr::FieldExpr(it) => &it.syntax, @@ -8726,6 +8837,10 @@ impl From for Pat { #[inline] fn from(node: BoxPat) -> Pat { Pat::BoxPat(node) } } +impl From for Pat { + #[inline] + fn from(node: CfgPredPat) -> Pat { Pat::CfgPredPat(node) } +} impl From for Pat { #[inline] fn from(node: ConstBlockPat) -> Pat { Pat::ConstBlockPat(node) } @@ -8800,6 +8915,7 @@ impl AstNode for Pat { matches!( kind, BOX_PAT + | CFG_PRED_PAT | CONST_BLOCK_PAT | DEREF_PAT | IDENT_PAT @@ -8823,6 +8939,7 @@ impl AstNode for Pat { fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { BOX_PAT => Pat::BoxPat(BoxPat { syntax }), + CFG_PRED_PAT => Pat::CfgPredPat(CfgPredPat { syntax }), CONST_BLOCK_PAT => Pat::ConstBlockPat(ConstBlockPat { syntax }), DEREF_PAT => Pat::DerefPat(DerefPat { syntax }), IDENT_PAT => Pat::IdentPat(IdentPat { syntax }), @@ -8848,6 +8965,7 @@ impl AstNode for Pat { fn syntax(&self) -> &SyntaxNode { match self { Pat::BoxPat(it) => &it.syntax, + Pat::CfgPredPat(it) => &it.syntax, Pat::ConstBlockPat(it) => &it.syntax, Pat::DerefPat(it) => &it.syntax, Pat::IdentPat(it) => &it.syntax, @@ -10222,6 +10340,16 @@ impl std::fmt::Display for CfgMeta { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for CfgPredExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for CfgPredPat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for ClosureExpr { 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/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs index f473df9b1548f..176e86ab220e0 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs @@ -135,7 +135,8 @@ pub fn precedence(expr: &ast::Expr) -> ExprPrecedence { | Expr::TupleExpr(_) | Expr::UnderscoreExpr(_) | Expr::WhileExpr(_) - | Expr::IncludeBytesExpr(_) => ExprPrecedence::Unambiguous, + | Expr::IncludeBytesExpr(_) + | Expr::CfgPredExpr(_) => ExprPrecedence::Unambiguous, } } @@ -384,7 +385,7 @@ impl Expr { ArrayExpr(_) | TupleExpr(_) | Literal(_) | PathExpr(_) | ParenExpr(_) | IfExpr(_) | WhileExpr(_) | ForExpr(_) | LoopExpr(_) | MatchExpr(_) | BlockExpr(_) - | RecordExpr(_) | UnderscoreExpr(_) | IncludeBytesExpr(_) => (0, 0), + | RecordExpr(_) | UnderscoreExpr(_) | IncludeBytesExpr(_) | CfgPredExpr(_) => (0, 0), } } @@ -529,7 +530,7 @@ impl Expr { ArrayExpr(_) | TupleExpr(_) | Literal(_) | PathExpr(_) | ParenExpr(_) | IfExpr(_) | WhileExpr(_) | ForExpr(_) | LoopExpr(_) | MatchExpr(_) | BlockExpr(_) | RecordExpr(_) | UnderscoreExpr(_) | MacroExpr(_) - | IncludeBytesExpr(_) => None, + | IncludeBytesExpr(_) | CfgPredExpr(_) => None, }; token.map(|t| t.text_range()).unwrap_or_else(|| this.syntax().text_range()).start() @@ -544,7 +545,7 @@ impl Expr { | ClosureExpr(_) | FieldExpr(_) | IndexExpr(_) | Literal(_) | LoopExpr(_) | MacroExpr(_) | MethodCallExpr(_) | ParenExpr(_) | PathExpr(_) | RecordExpr(_) | TryExpr(_) | TupleExpr(_) | UnderscoreExpr(_) | OffsetOfExpr(_) - | FormatArgsExpr(_) | AsmExpr(_) => false, + | FormatArgsExpr(_) | AsmExpr(_) | CfgPredExpr(_) => false, // For BinExpr and RangeExpr this is technically wrong -- the child can be on the left... BinExpr(_) | RangeExpr(_) | BreakExpr(_) | ContinueExpr(_) | PrefixExpr(_) 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 129e63d3988b5..b978464a4908a 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -85,6 +85,7 @@ //! column: //! addr_of: //! offset_of: +//! cfg: #![rustc_coherence_is_core] #![feature(lang_items)] @@ -2275,6 +2276,16 @@ mod macros { } // endregion:include_bytes + // region:cfg + #[rustc_builtin_macro] + #[macro_export] + macro_rules! cfg { + ($($cfg:tt)*) => { + /* compiler built-in */ + }; + } + // endregion:cfg + // region:concat #[rustc_builtin_macro] #[macro_export] @@ -2497,6 +2508,7 @@ pub mod prelude { include_bytes, // :include_bytes concat, // :concat env, option_env, // :env + cfg, // :cfg matches, // :matches }; } diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index 6465a85d2d29b..2d5d4c14bc266 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -319,6 +319,7 @@ impl NotifyActor { let depth = entry.depth(); let is_dir = entry.file_type().is_dir(); let is_file = entry.file_type().is_file(); + let is_symlink = entry.path_is_symlink(); let abs_path = AbsPathBuf::try_from( Utf8PathBuf::from_path_buf(entry.into_path()).ok()?, ) @@ -326,7 +327,9 @@ impl NotifyActor { if depth < 2 && is_dir { send_message(abs_path.clone()); } - if is_dir && do_watch { + // Recursively watch each include root (depth = 0). Also watch any symlinked + // directories because macOS fsevent does not follow symlinks on its own. + if is_dir && do_watch && (depth == 0 || is_symlink) { watch(abs_path.as_ref()); } if !is_file { diff --git a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs index eb55081c2038e..18359b9f685b6 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs @@ -134,7 +134,6 @@ impl VfsPath { #[cfg(windows)] { use windows_paths::Encode; - let path: &std::path::Path = path.as_ref(); let components = path.components(); let mut add_sep = false; for component in components { @@ -143,30 +142,25 @@ impl VfsPath { } let len_before = buf.len(); match component { - std::path::Component::Prefix(prefix) => { + paths::Utf8Component::Prefix(prefix) => { // kind() returns a normalized and comparable path prefix. prefix.kind().encode(buf); } - std::path::Component::RootDir => { + paths::Utf8Component::RootDir => { if !add_sep { - component.as_os_str().encode(buf); + component.as_str().encode(buf); } } - _ => component.as_os_str().encode(buf), + _ => component.as_str().encode(buf), } // some components may be encoded empty add_sep = len_before != buf.len(); } } - #[cfg(unix)] + #[cfg(not(windows))] { - use std::os::unix::ffi::OsStrExt; - buf.extend(path.as_os_str().as_bytes()); - } - #[cfg(not(any(windows, unix)))] - { - buf.extend(path.as_os_str().to_string_lossy().as_bytes()); + buf.extend(path.as_str().as_bytes()); } } VfsPathRepr::VirtualPath(VirtualPath(s)) => buf.extend(s.as_bytes()), @@ -180,28 +174,15 @@ mod windows_paths { fn encode(&self, buf: &mut Vec); } - impl Encode for std::ffi::OsStr { - fn encode(&self, buf: &mut Vec) { - use std::os::windows::ffi::OsStrExt; - for wchar in self.encode_wide() { - buf.extend(wchar.to_le_bytes().iter().copied()); - } - } - } - impl Encode for u8 { fn encode(&self, buf: &mut Vec) { - let wide = *self as u16; - buf.extend(wide.to_le_bytes().iter().copied()) + buf.push(*self); } } impl Encode for &str { fn encode(&self, buf: &mut Vec) { - debug_assert!(self.is_ascii()); - for b in self.as_bytes() { - b.encode(buf) - } + buf.extend(self.as_bytes()); } } @@ -211,14 +192,14 @@ mod windows_paths { const DEVICE: &str = "\\\\.\\"; const COLON: &str = ":"; - impl Encode for std::path::Prefix<'_> { + impl Encode for paths::Utf8Prefix<'_> { fn encode(&self, buf: &mut Vec) { match self { - std::path::Prefix::Verbatim(c) => { + paths::Utf8Prefix::Verbatim(c) => { VERBATIM.encode(buf); c.encode(buf); } - std::path::Prefix::VerbatimUNC(server, share) => { + paths::Utf8Prefix::VerbatimUNC(server, share) => { VERBATIM.encode(buf); UNC.encode(buf); SEP.encode(buf); @@ -226,23 +207,23 @@ mod windows_paths { SEP.encode(buf); share.encode(buf); } - std::path::Prefix::VerbatimDisk(d) => { + paths::Utf8Prefix::VerbatimDisk(d) => { VERBATIM.encode(buf); d.encode(buf); COLON.encode(buf); } - std::path::Prefix::DeviceNS(device) => { + paths::Utf8Prefix::DeviceNS(device) => { DEVICE.encode(buf); device.encode(buf); } - std::path::Prefix::UNC(server, share) => { + paths::Utf8Prefix::UNC(server, share) => { SEP.encode(buf); SEP.encode(buf); server.encode(buf); SEP.encode(buf); share.encode(buf); } - std::path::Prefix::Disk(d) => { + paths::Utf8Prefix::Disk(d) => { d.encode(buf); COLON.encode(buf); } @@ -269,7 +250,7 @@ mod windows_paths { fn test_sep_root_dir_encoding() { let mut buf = Vec::new(); vfs("C:/x/y").encode(&mut buf); - assert_eq!(&buf, &[0, 67, 0, 58, 0, 92, 0, 120, 0, 92, 0, 121, 0]) + assert_eq!(&buf, &[0, 67, 58, 92, 120, 92, 121]) } #[cfg(test)] diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 1c4642751f48e..e45b7f8b3ec8c 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -585,6 +585,13 @@ Default: `1000` Term search fuel in "units of work" for autocompletion (Defaults to 1000). +## rust-analyzer.completion.turbofish.enable {#completion.turbofish.enable} + +Default: `true` + +Complete turbofish (`::<>`) at the call site (if needed). + + ## rust-analyzer.diagnostics.disabled {#diagnostics.disabled} Default: `[]` diff --git a/src/tools/rust-analyzer/editors/code/eslint.config.mts b/src/tools/rust-analyzer/editors/code/eslint.config.mts index 3520593eef285..4e63563f18b26 100644 --- a/src/tools/rust-analyzer/editors/code/eslint.config.mts +++ b/src/tools/rust-analyzer/editors/code/eslint.config.mts @@ -3,9 +3,9 @@ import stylistic from "@stylistic/eslint-plugin"; import eslint from "@eslint/js"; import tseslint from "typescript-eslint"; import stylisticJs from "@stylistic/eslint-plugin-js"; -import { type FlatESLintConfig } from "eslint-define-config"; +import { type Config } from "eslint/config"; -const config: FlatESLintConfig[] = [ +const config: Config[] = [ eslintConfigPrettier, eslint.configs.recommended, stylisticJs.configs["disable-legacy"], diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 3037deb05676e..5789fc2ed23bd 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -31,7 +31,6 @@ "esbuild": "^0.25.0", "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", - "eslint-define-config": "^2.1.0", "ovsx": "0.10.10", "prettier": "^3.8.1", "tslib": "^2.8.1", @@ -3768,29 +3767,6 @@ "eslint": ">=7.0.0" } }, - "node_modules/eslint-define-config": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-define-config/-/eslint-define-config-2.1.0.tgz", - "integrity": "sha512-QUp6pM9pjKEVannNAbSJNeRuYwW3LshejfyBBpjeMGaJjaDUpVps4C6KVR8R7dWZnD3i0synmrE36znjTkJvdQ==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/Shinigami92" - }, - { - "type": "paypal", - "url": "https://www.paypal.com/donate/?hosted_button_id=L7GY729FBKTZY" - } - ], - "license": "MIT", - "engines": { - "node": ">=18.0.0", - "npm": ">=9.0.0", - "pnpm": ">=8.6.0" - } - }, "node_modules/eslint-scope": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index de9f17b9ccbf8..eea06eacf4423 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -71,7 +71,6 @@ "esbuild": "^0.25.0", "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", - "eslint-define-config": "^2.1.0", "ovsx": "0.10.10", "prettier": "^3.8.1", "tslib": "^2.8.1", @@ -696,7 +695,7 @@ }, "rust-analyzer.trace.extension": { "description": "Enable logging of VS Code extensions itself.", - "markdownDeprecationMessage": "Log level is now controlled by the [Developer: Set Log Level...](command:workbench.action.setLogLevel) command.You can set the log level for the current session and also the default log level from there. This is also available by clicking the gear icon on the OUTPUT tab when Rust Analyzer Client is visible or by passing the --log rust-lang.rust-analyzer:debug parameter to VS Code.", + "markdownDeprecationMessage": "Log level is now controlled by the [Developer: Set Log Level...](command:workbench.action.setLogLevel) command. You can set the log level for the current session and also the default log level from there. This is also available by clicking the gear icon on the OUTPUT tab when rust-analyzer Extension is visible or by passing the --log rust-lang.rust-analyzer:debug parameter to VS Code.", "type": "boolean", "default": false } @@ -1571,6 +1570,16 @@ } } }, + { + "title": "Completion", + "properties": { + "rust-analyzer.completion.turbofish.enable": { + "markdownDescription": "Complete turbofish (`::<>`) at the call site (if needed).", + "default": true, + "type": "boolean" + } + } + }, { "title": "Diagnostics", "properties": { diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index 440f21cbe3c50..5a5ddab155d46 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -21,12 +21,12 @@ export async function bootstrap( log.info("Using server binary at", path); - if (!isValidExecutable(path, config.serverExtraEnv)) { + if (!(await isValidExecutable(path, config.serverExtraEnv))) { throw new Error( `Failed to execute ${path} --version.` + (config.serverPath - ? `\`config.server.path\` or \`config.serverPath\` has been set explicitly.\ - Consider removing this config or making a valid server binary available at that path.` + ? ` \`config.server.path\` or \`config.serverPath\` has been set explicitly.` + + ` Consider removing this config or making a valid server binary available at that path.` : ""), ); } diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index e07fbb8bead68..c6e20259399d9 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -287,9 +287,9 @@ export class Ctx implements RustAnalyzerExtensionApi { let message = "bootstrap error. "; message += - 'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically).'; + 'See the logs in "OUTPUT > rust-analyzer Extension" (should open automatically).'; message += - 'To enable verbose logs, click the gear icon in the "OUTPUT" tab and select "Debug".'; + ' To enable verbose logs, click the gear icon in the "OUTPUT" tab and select "Debug".'; log.error("Bootstrap error", err); throw new Error(message); diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index 7136314e7ca2c..f586a800b8696 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -5,7 +5,7 @@ import * as commands from "./commands"; import { type CommandFactory, Ctx, fetchWorkspace } from "./ctx"; import * as diagnostics from "./diagnostics"; import { activateTaskProvider } from "./tasks"; -import { setContextValue } from "./util"; +import { log, setContextValue } from "./util"; import { initializeDebugSessionTrackingAndRebuild } from "./debug"; const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; @@ -118,7 +118,19 @@ async function activateServer(ctx: Ctx): Promise { health: "stopped", }); } else { - await ctx.start(); + try { + await ctx.start(); + } catch (err) { + // Continue extension activation with the server stopped, as if the server crashed. + await ctx.stopAndDispose(); + ctx.setServerStatus({ + health: "stopped", + }); + log.error("Failed to start the rust-analyzer server", err); + void vscode.window.showErrorMessage( + `Cannot start rust-analyzer server: ${err instanceof Error ? err.message : err}. `, + ); + } } return ctx; diff --git a/src/tools/rust-analyzer/lib/README.md b/src/tools/rust-analyzer/lib/README.md index d420eeb960515..f9938f9df5bb7 100644 --- a/src/tools/rust-analyzer/lib/README.md +++ b/src/tools/rust-analyzer/lib/README.md @@ -12,3 +12,8 @@ To release new versions of these packages, change their version in Cargo.toml. O While prototyping, the local versions can be used by uncommenting the relevant lines in the `[patch.'crates-io']` section in Cargo.toml + +## Publishing of the crates + +The crates are published using [Trusted publishing](https://crates.io/docs/trusted-publishing). If a new lib crate is added +in this directory, it also has to be recorded in the [team database](https://github.com/rust-lang/team/blob/main/repos/rust-lang/rust-analyzer.toml). diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 18fea436747c7..c99a43a763644 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -32d94cc9be3f6e6c3fa1deaea9e0ab93c4980dba +03cbd412d98691c83227b65eddadb7b01db4a190 diff --git a/src/tools/rust-analyzer/xtask/src/codegen.rs b/src/tools/rust-analyzer/xtask/src/codegen.rs index 482b31e306cab..f8a7f1875539a 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen.rs @@ -132,12 +132,19 @@ fn reformat(text: String) -> String { let rustfmt_toml = project_root().join("rustfmt.toml"); let toolchain = &std::env::var("RUSTFMT_TOOLCHAIN").unwrap_or("stable".to_owned()); let version = cmd!(sh, "rustup run {toolchain} rustfmt --version").read().unwrap_or_default(); + let toolchain_matches = |version: &str| { + if version.contains(toolchain) { + return true; + } + // Nightly date cannot be accurately matched + toolchain.starts_with("nightly-") && version.contains("nightly") + }; // First try explicitly requesting the stable channel via rustup in case nightly is being used by default, // then plain rustfmt in case rustup isn't being used to manage the compiler (e.g. when using Nix). - let mut stdout = if !version.contains(toolchain) { + let mut stdout = if !toolchain_matches(&version) { let version = cmd!(sh, "rustfmt --version").read().unwrap_or_default(); - if !version.contains(toolchain) { + if !toolchain_matches(&version) { panic!( "Failed to run rustfmt from toolchain '{toolchain}'. \ Please run `rustup component add rustfmt --toolchain {toolchain}` to install it.", diff --git a/tests/codegen-llvm/virtual-attrs.rs b/tests/codegen-llvm/virtual-attrs.rs new file mode 100644 index 0000000000000..1cfaee913dd5c --- /dev/null +++ b/tests/codegen-llvm/virtual-attrs.rs @@ -0,0 +1,20 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/163856. +//! +//! Checks that reify shim containing a virtual call, does NOT inherit optimization attributes from +//! the method. Neither those deduced from a MIR body, nor #[rustc_nounwind] attribute. +//@ compile-flags: -O -C no-prepopulate-passes +//@ needs-unwind + +#![crate_type = "lib"] +#![feature(rustc_attrs)] + +pub trait Foo { + #[rustc_nounwind] + fn foo(&self, _: [u8; 1024]) {} +} +// CHECK-LABEL: ; ::foo::{shim:reify#0} +// CHECK-NEXT: ; Function Attrs: +// CHECK-NOT: nounwind +// Notably readonly and captures(none) should NOT be present on the last argument +// CHECK-NEXT: define {{.*}}({{.*}}, ptr{{( dead_on_return)?}} noalias nofree noundef align 1 captures(address){{( dead_on_return)?}} dereferenceable(1024) %_2) +pub static A: fn(&'static dyn Foo, [u8; 1024]) = ::foo; diff --git a/tests/debuginfo/pretty-std/lldb_input/non_windows.json b/tests/debuginfo/pretty-std/lldb_input/non_windows.json index 04106aed96d7f..737eb5004b2bf 100644 --- a/tests/debuginfo/pretty-std/lldb_input/non_windows.json +++ b/tests/debuginfo/pretty-std/lldb_input/non_windows.json @@ -1,6 +1,6 @@ { "bless_metadata": { - "python_version": "3.14.5 (main, May 11 2026, 00:00:00) [GCC 16.1.1 20260501 (Red Hat 16.1.1-1)]", + "python_version": "3.14.8 (main, Oct 1 2026, 00:00:00) [GCC 16.2.1 20260819 (Red Hat 16.2.1-2)]", "debugger_version": "lldb version 22.1.8", "feature_flags": "LLDBFeature.StaticFields|TypeRecognizers|Float128" }, @@ -137,10 +137,10 @@ 110, 103, 32, - -16, - -97, - -104, - -125 + 240, + 159, + 152, + 131 ] } } @@ -167,37 +167,43 @@ } ] }, - "int *": { - "size": 8, - "type_class": 4096 - }, - "unsigned long": { - "size": 8, - "type_class": 4, - "basic_type": 16 + "&str": { + "size": 16, + "type_class": 16384, + "fields": [ + { + "name": "data_ptr", + "type": "unsigned char *", + "offset": 0 + }, + { + "name": "length", + "type": "unsigned long", + "offset": 8 + } + ] }, - "int": { - "size": 4, - "type_class": 4, - "basic_type": 13 + "alloc::alloc::Global": { + "size": 0, + "type_class": 16384 }, - "alloc::vec::Vec": { - "size": 24, + "alloc::raw_vec::RawVec": { + "size": 16, "type_class": 16384, "fields": [ { - "name": "buf", - "type": "alloc::raw_vec::RawVec", + "name": "inner", + "type": "alloc::raw_vec::RawVecInner", "offset": 0 }, { - "name": "len", - "type": "unsigned long", + "name": "_marker", + "type": "core::marker::PhantomData", "offset": 16 } ], "generic_params": [ - "unsigned long", + "unsigned char", "alloc::alloc::Global" ] }, @@ -245,74 +251,6 @@ "alloc::alloc::Global" ] }, - "core::ptr::non_null::NonNull": { - "size": 8, - "type_class": 16384, - "fields": [ - { - "name": "pointer", - "type": "unsigned char *", - "offset": 0 - } - ], - "generic_params": [ - "unsigned char" - ] - }, - "unsigned char *": { - "size": 8, - "type_class": 4096 - }, - "unsigned char": { - "size": 1, - "type_class": 4, - "basic_type": 4 - }, - "core::marker::PhantomData": { - "size": 0, - "type_class": 16384, - "generic_params": [ - "unsigned char" - ] - }, - "core::num::niche_types::UsizeNoHighBit": { - "size": 8, - "type_class": 16384, - "fields": [ - { - "name": "__0", - "type": "unsigned long", - "offset": 0 - } - ] - }, - "alloc::alloc::Global": { - "size": 0, - "type_class": 16384 - }, - "core::marker::PhantomData": { - "size": 0, - "type_class": 16384, - "generic_params": [ - "unsigned long" - ] - }, - "&str": { - "size": 16, - "type_class": 16384, - "fields": [ - { - "name": "data_ptr", - "type": "unsigned char *", - "offset": 0 - }, - { - "name": "length", - "type": "unsigned long", - "offset": 8 - } - ] - }, "alloc::string::String": { "size": 24, "type_class": 16384, @@ -344,26 +282,51 @@ "alloc::alloc::Global" ] }, - "alloc::raw_vec::RawVec": { - "size": 16, + "alloc::vec::Vec": { + "size": 24, "type_class": 16384, "fields": [ { - "name": "inner", - "type": "alloc::raw_vec::RawVecInner", + "name": "buf", + "type": "alloc::raw_vec::RawVec", "offset": 0 }, { - "name": "_marker", - "type": "core::marker::PhantomData", + "name": "len", + "type": "unsigned long", "offset": 16 } ], "generic_params": [ - "unsigned char", + "unsigned long", "alloc::alloc::Global" ] }, + "core::marker::PhantomData": { + "size": 0, + "type_class": 16384, + "generic_params": [ + "unsigned char" + ] + }, + "core::marker::PhantomData": { + "size": 0, + "type_class": 16384, + "generic_params": [ + "unsigned long" + ] + }, + "core::num::niche_types::UsizeNoHighBit": { + "size": 8, + "type_class": 16384, + "fields": [ + { + "name": "__0", + "type": "unsigned long", + "offset": 0 + } + ] + }, "core::option::Option": { "size": 4, "type_class": 16384, @@ -375,6 +338,27 @@ } ] }, + "core::option::Option::None": { + "size": 4, + "type_class": 16384, + "generic_params": [ + "short" + ] + }, + "core::option::Option::Some": { + "size": 4, + "type_class": 16384, + "fields": [ + { + "name": "__0", + "type": "short", + "offset": 2 + } + ], + "generic_params": [ + "short" + ] + }, "core::option::Option::core::option::Option$Inner": { "size": 4, "type_class": 65536, @@ -407,23 +391,6 @@ } ] }, - "unsigned short": { - "size": 2, - "type_class": 4, - "basic_type": 12 - }, - "core::option::Option::None": { - "size": 4, - "type_class": 16384, - "generic_params": [ - "short" - ] - }, - "short": { - "size": 2, - "type_class": 4, - "basic_type": 11 - }, "core::option::Option::core::option::Option$Inner::Some$Variant": { "size": 4, "type_class": 16384, @@ -440,29 +407,36 @@ } ] }, - "core::option::Option::Some": { - "size": 4, + "core::option::Option": { + "size": 16, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "short", - "offset": 2 + "name": "$variants$", + "type": "core::option::Option::core::option::Option$Inner", + "offset": 0 } - ], + ] + }, + "core::option::Option::None": { + "size": 16, + "type_class": 16384, "generic_params": [ - "short" + "long" ] }, - "core::option::Option": { + "core::option::Option::Some": { "size": 16, "type_class": 16384, "fields": [ { - "name": "$variants$", - "type": "core::option::Option::core::option::Option$Inner", - "offset": 0 + "name": "__0", + "type": "long", + "offset": 8 } + ], + "generic_params": [ + "long" ] }, "core::option::Option::core::option::Option$Inner": { @@ -497,18 +471,6 @@ } ] }, - "core::option::Option::None": { - "size": 16, - "type_class": 16384, - "generic_params": [ - "long" - ] - }, - "long": { - "size": 8, - "type_class": 4, - "basic_type": 15 - }, "core::option::Option::core::option::Option$Inner::Some$Variant": { "size": 16, "type_class": 16384, @@ -525,20 +487,39 @@ } ] }, - "core::option::Option::Some": { - "size": 16, + "core::ptr::non_null::NonNull": { + "size": 8, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "long", - "offset": 8 + "name": "pointer", + "type": "unsigned char *", + "offset": 0 } ], "generic_params": [ - "long" + "unsigned char" ] }, + "int": { + "size": 4, + "type_class": 4, + "basic_type": 13 + }, + "int *": { + "size": 8, + "type_class": 4096 + }, + "long": { + "size": 8, + "type_class": 4, + "basic_type": 15 + }, + "short": { + "size": 2, + "type_class": 4, + "basic_type": 11 + }, "std::ffi::os_str::OsString": { "size": 24, "type_class": 16384, @@ -560,6 +541,25 @@ "offset": 0 } ] + }, + "unsigned char": { + "size": 1, + "type_class": 4, + "basic_type": 4 + }, + "unsigned char *": { + "size": 8, + "type_class": 4096 + }, + "unsigned long": { + "size": 8, + "type_class": 4, + "basic_type": 16 + }, + "unsigned short": { + "size": 2, + "type_class": 4, + "basic_type": 12 } } } diff --git a/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json b/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json index c0f831e31bfe0..7f7772bd4caa1 100644 --- a/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json +++ b/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json @@ -1,7 +1,7 @@ { "bless_metadata": { "python_version": "3.11.9 (tags/v3.11.9:de54cf5, Apr 2 2024, 10:12:12) [MSC v.1938 64 bit (AMD64)]", - "debugger_version": "lldb version 22.1.2 (https://github.com/llvm/llvm-project revision 1ab49a973e210e97d61e5db6557180dcb92c3e98)\n clang revision 1ab49a973e210e97d61e5db6557180dcb92c3e98\n llvm revision 1ab49a973e210e97d61e5db6557180dcb92c3e98", + "debugger_version": "lldb version 22.1.8 (https://github.com/llvm/llvm-project revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1)\n clang revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1\n llvm revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1", "feature_flags": "LLDBFeature.StaticFields|TypeRecognizers|Float128" }, "breakpoints": [ @@ -141,10 +141,10 @@ 110, 103, 32, - -16, - -97, - -104, - -125 + 240, + 159, + 152, + 131 ] } }, @@ -178,37 +178,43 @@ } ] }, - "int *": { - "size": 8, - "type_class": 4096 - }, - "unsigned long long": { - "size": 8, - "type_class": 4, - "basic_type": 18 + "&str": { + "size": 16, + "type_class": 16384, + "fields": [ + { + "name": "data_ptr", + "type": "unsigned char *", + "offset": 0 + }, + { + "name": "length", + "type": "unsigned long long", + "offset": 8 + } + ] }, - "int": { - "size": 4, - "type_class": 4, - "basic_type": 13 + "alloc::alloc::Global": { + "size": 0, + "type_class": 16384 }, - "alloc::vec::Vec": { - "size": 24, + "alloc::raw_vec::RawVec": { + "size": 16, "type_class": 16384, "fields": [ { - "name": "buf", - "type": "alloc::raw_vec::RawVec", + "name": "inner", + "type": "alloc::raw_vec::RawVecInner", "offset": 0 }, { - "name": "len", - "type": "unsigned long long", + "name": "_marker", + "type": "core::marker::PhantomData", "offset": 16 } ], "generic_params": [ - "unsigned long long", + "unsigned char", "alloc::alloc::Global" ] }, @@ -256,74 +262,6 @@ "alloc::alloc::Global" ] }, - "core::ptr::non_null::NonNull": { - "size": 8, - "type_class": 16384, - "fields": [ - { - "name": "pointer", - "type": "unsigned char *", - "offset": 0 - } - ], - "generic_params": [ - "unsigned char" - ] - }, - "unsigned char *": { - "size": 8, - "type_class": 4096 - }, - "unsigned char": { - "size": 1, - "type_class": 4, - "basic_type": 4 - }, - "core::marker::PhantomData": { - "size": 0, - "type_class": 16384, - "generic_params": [ - "unsigned char" - ] - }, - "core::num::niche_types::UsizeNoHighBit": { - "size": 8, - "type_class": 16384, - "fields": [ - { - "name": "__0", - "type": "unsigned long long", - "offset": 0 - } - ] - }, - "alloc::alloc::Global": { - "size": 0, - "type_class": 16384 - }, - "core::marker::PhantomData": { - "size": 0, - "type_class": 16384, - "generic_params": [ - "unsigned long long" - ] - }, - "&str": { - "size": 16, - "type_class": 16384, - "fields": [ - { - "name": "data_ptr", - "type": "unsigned char *", - "offset": 0 - }, - { - "name": "length", - "type": "unsigned long long", - "offset": 8 - } - ] - }, "alloc::string::String": { "size": 24, "type_class": 16384, @@ -355,26 +293,72 @@ "alloc::alloc::Global" ] }, - "alloc::raw_vec::RawVec": { - "size": 16, + "alloc::vec::Vec": { + "size": 24, "type_class": 16384, "fields": [ { - "name": "inner", - "type": "alloc::raw_vec::RawVecInner", + "name": "buf", + "type": "alloc::raw_vec::RawVec", "offset": 0 }, { - "name": "_marker", - "type": "core::marker::PhantomData", + "name": "len", + "type": "unsigned long long", "offset": 16 } ], "generic_params": [ - "unsigned char", + "unsigned long long", "alloc::alloc::Global" ] }, + "alloc::wtf8::Wtf8Buf": { + "size": 32, + "type_class": 16384, + "fields": [ + { + "name": "bytes", + "type": "alloc::vec::Vec", + "offset": 0 + }, + { + "name": "is_known_utf8", + "type": "bool", + "offset": 24 + } + ] + }, + "bool": { + "size": 1, + "type_class": 4, + "basic_type": 21 + }, + "core::marker::PhantomData": { + "size": 0, + "type_class": 16384, + "generic_params": [ + "unsigned char" + ] + }, + "core::marker::PhantomData": { + "size": 0, + "type_class": 16384, + "generic_params": [ + "unsigned long long" + ] + }, + "core::num::niche_types::UsizeNoHighBit": { + "size": 8, + "type_class": 16384, + "fields": [ + { + "name": "__0", + "type": "unsigned long long", + "offset": 0 + } + ] + }, "core::option::Option": { "size": 4, "type_class": 16384, @@ -386,6 +370,27 @@ } ] }, + "core::option::Option::None": { + "size": 4, + "type_class": 16384, + "generic_params": [ + "short" + ] + }, + "core::option::Option::Some": { + "size": 4, + "type_class": 16384, + "fields": [ + { + "name": "__0", + "type": "short", + "offset": 2 + } + ], + "generic_params": [ + "short" + ] + }, "core::option::Option::core::option::Option$Inner": { "size": 6, "type_class": 65536, @@ -418,23 +423,6 @@ } ] }, - "unsigned short": { - "size": 2, - "type_class": 4, - "basic_type": 12 - }, - "core::option::Option::None": { - "size": 4, - "type_class": 16384, - "generic_params": [ - "short" - ] - }, - "short": { - "size": 2, - "type_class": 4, - "basic_type": 11 - }, "core::option::Option::core::option::Option$Inner::Some$Variant": { "size": 6, "type_class": 16384, @@ -451,29 +439,36 @@ } ] }, - "core::option::Option::Some": { - "size": 4, + "core::option::Option": { + "size": 16, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "short", - "offset": 2 + "name": "$variants$", + "type": "core::option::Option::core::option::Option$Inner", + "offset": 0 } - ], + ] + }, + "core::option::Option::None": { + "size": 16, + "type_class": 16384, "generic_params": [ - "short" + "long long" ] }, - "core::option::Option": { + "core::option::Option::Some": { "size": 16, "type_class": 16384, "fields": [ { - "name": "$variants$", - "type": "core::option::Option::core::option::Option$Inner", - "offset": 0 + "name": "__0", + "type": "long long", + "offset": 8 } + ], + "generic_params": [ + "long long" ] }, "core::option::Option::core::option::Option$Inner": { @@ -508,18 +503,6 @@ } ] }, - "core::option::Option::None": { - "size": 16, - "type_class": 16384, - "generic_params": [ - "long long" - ] - }, - "long long": { - "size": 8, - "type_class": 4, - "basic_type": 17 - }, "core::option::Option::core::option::Option$Inner::Some$Variant": { "size": 24, "type_class": 16384, @@ -536,20 +519,39 @@ } ] }, - "core::option::Option::Some": { - "size": 16, + "core::ptr::non_null::NonNull": { + "size": 8, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "long long", - "offset": 8 + "name": "pointer", + "type": "unsigned char *", + "offset": 0 } ], "generic_params": [ - "long long" + "unsigned char" ] }, + "int": { + "size": 4, + "type_class": 4, + "basic_type": 13 + }, + "int *": { + "size": 8, + "type_class": 4096 + }, + "long long": { + "size": 8, + "type_class": 4, + "basic_type": 17 + }, + "short": { + "size": 2, + "type_class": 4, + "basic_type": 11 + }, "std::ffi::os_str::OsString": { "size": 32, "type_class": 16384, @@ -572,26 +574,24 @@ } ] }, - "alloc::wtf8::Wtf8Buf": { - "size": 32, - "type_class": 16384, - "fields": [ - { - "name": "bytes", - "type": "alloc::vec::Vec", - "offset": 0 - }, - { - "name": "is_known_utf8", - "type": "bool", - "offset": 24 - } - ] - }, - "bool": { + "unsigned char": { "size": 1, "type_class": 4, - "basic_type": 21 + "basic_type": 4 + }, + "unsigned char *": { + "size": 8, + "type_class": 4096 + }, + "unsigned long long": { + "size": 8, + "type_class": 4, + "basic_type": 18 + }, + "unsigned short": { + "size": 2, + "type_class": 4, + "basic_type": 12 } } } diff --git a/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json b/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json index 78ce149f65c66..2409a3b0d4a69 100644 --- a/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json +++ b/tests/debuginfo/pretty-std/lldb_input/windows_msvc.json @@ -1,7 +1,7 @@ { "bless_metadata": { "python_version": "3.11.9 (tags/v3.11.9:de54cf5, Apr 2 2024, 10:12:12) [MSC v.1938 64 bit (AMD64)]", - "debugger_version": "lldb version 22.1.2 (https://github.com/llvm/llvm-project revision 1ab49a973e210e97d61e5db6557180dcb92c3e98)\n clang revision 1ab49a973e210e97d61e5db6557180dcb92c3e98\n llvm revision 1ab49a973e210e97d61e5db6557180dcb92c3e98", + "debugger_version": "lldb version 22.1.8 (https://github.com/llvm/llvm-project revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1)\n clang revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1\n llvm revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1", "feature_flags": "LLDBFeature.StaticFields|TypeRecognizers|Float128" }, "breakpoints": [ @@ -145,10 +145,10 @@ 110, 103, 32, - -16, - -97, - -104, - -125 + 240, + 159, + 152, + 131 ] } }, @@ -166,57 +166,18 @@ } ], "types": { - "ref$ >": { - "size": 16, - "type_class": 16384, - "fields": [ - { - "name": "data_ptr", - "type": "int *", - "offset": 0 - }, - { - "name": "length", - "type": "unsigned long long", - "offset": 8 - } - ], - "generic_params": [ - "long" - ] - }, - "int *": { - "size": 8, - "type_class": 4096 - }, - "unsigned long long": { - "size": 8, - "type_class": 4, - "basic_type": 18 - }, - "long": { - "size": 4, - "type_class": 4, - "basic_type": 15 - }, - "int": { - "size": 4, - "type_class": 4, - "basic_type": 13 + "alloc::alloc::Global": { + "size": 0, + "type_class": 16384 }, - "alloc::vec::Vec": { - "size": 24, + "alloc::raw_vec::RawVec": { + "size": 16, "type_class": 16384, "fields": [ { - "name": "buf", - "type": "alloc::raw_vec::RawVec", + "name": "inner", + "type": "alloc::raw_vec::RawVecInner", "offset": 0 - }, - { - "name": "len", - "type": "unsigned long long", - "offset": 16 } ], "generic_params": [ @@ -224,7 +185,7 @@ "alloc::alloc::Global" ] }, - "alloc::raw_vec::RawVec": { + "alloc::raw_vec::RawVec": { "size": 16, "type_class": 16384, "fields": [ @@ -235,7 +196,7 @@ } ], "generic_params": [ - "unsigned long long", + "unsigned char", "alloc::alloc::Global" ] }, @@ -258,104 +219,101 @@ "alloc::alloc::Global" ] }, - "core::num::niche_types::UsizeNoHighBit": { - "size": 8, + "alloc::string::String": { + "size": 24, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "unsigned long long", + "name": "vec", + "type": "alloc::vec::Vec", "offset": 0 } ] }, - "core::ptr::non_null::NonNull": { - "size": 8, + "alloc::vec::Vec": { + "size": 24, "type_class": 16384, "fields": [ { - "name": "pointer", - "type": "unsigned char *", + "name": "buf", + "type": "alloc::raw_vec::RawVec", "offset": 0 + }, + { + "name": "len", + "type": "unsigned long long", + "offset": 16 } ], "generic_params": [ - "unsigned char" + "unsigned long long", + "alloc::alloc::Global" ] }, - "unsigned char *": { - "size": 8, - "type_class": 4096 - }, - "unsigned char": { - "size": 1, - "type_class": 4, - "basic_type": 4 - }, - "alloc::alloc::Global": { - "size": 0, - "type_class": 16384 - }, - "ref$": { - "size": 16, + "alloc::vec::Vec": { + "size": 24, "type_class": 16384, "fields": [ { - "name": "data_ptr", - "type": "unsigned char *", + "name": "buf", + "type": "alloc::raw_vec::RawVec", "offset": 0 }, { - "name": "length", + "name": "len", "type": "unsigned long long", - "offset": 8 + "offset": 16 } + ], + "generic_params": [ + "unsigned char", + "alloc::alloc::Global" ] }, - "alloc::string::String": { - "size": 24, + "alloc::wtf8::Wtf8Buf": { + "size": 32, "type_class": 16384, "fields": [ { - "name": "vec", + "name": "bytes", "type": "alloc::vec::Vec", "offset": 0 + }, + { + "name": "is_known_utf8", + "type": "bool", + "offset": 24 } ] }, - "alloc::vec::Vec": { - "size": 24, + "bool": { + "size": 1, + "type_class": 4, + "basic_type": 21 + }, + "core::num::niche_types::UsizeNoHighBit": { + "size": 8, "type_class": 16384, "fields": [ { - "name": "buf", - "type": "alloc::raw_vec::RawVec", - "offset": 0 - }, - { - "name": "len", + "name": "__0", "type": "unsigned long long", - "offset": 16 + "offset": 0 } - ], - "generic_params": [ - "unsigned char", - "alloc::alloc::Global" ] }, - "alloc::raw_vec::RawVec": { - "size": 16, + "core::ptr::non_null::NonNull": { + "size": 8, "type_class": 16384, "fields": [ { - "name": "inner", - "type": "alloc::raw_vec::RawVecInner", + "name": "pointer", + "type": "unsigned char *", "offset": 0 } ], "generic_params": [ - "unsigned char", - "alloc::alloc::Global" + "unsigned char" ] }, "enum2$ >": { @@ -379,53 +337,43 @@ } ] }, - "enum2$ >::Variant0": { + "enum2$ >::None": { + "size": 4, + "type_class": 16384 + }, + "enum2$ >::Some": { "size": 4, "type_class": 16384, "fields": [ { - "name": "value", - "type": "enum2$ >::None", - "offset": 0 + "name": "__0", + "type": "short", + "offset": 2 } ] }, - "enum2$ >::None": { - "size": 4, - "type_class": 16384 - }, - "enum2$ >::Variant1": { + "enum2$ >::Variant0": { "size": 4, "type_class": 16384, "fields": [ { "name": "value", - "type": "enum2$ >::Some", + "type": "enum2$ >::None", "offset": 0 } ] }, - "enum2$ >::Some": { + "enum2$ >::Variant1": { "size": 4, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "short", - "offset": 2 + "name": "value", + "type": "enum2$ >::Some", + "offset": 0 } ] }, - "short": { - "size": 2, - "type_class": 4, - "basic_type": 11 - }, - "unsigned short": { - "size": 2, - "type_class": 4, - "basic_type": 12 - }, "enum2$ >": { "size": 16, "type_class": 65536, @@ -447,6 +395,21 @@ } ] }, + "enum2$ >::None": { + "size": 16, + "type_class": 16384 + }, + "enum2$ >::Some": { + "size": 16, + "type_class": 16384, + "fields": [ + { + "name": "__0", + "type": "long long", + "offset": 8 + } + ] + }, "enum2$ >::Variant0": { "size": 16, "type_class": 16384, @@ -458,10 +421,6 @@ } ] }, - "enum2$ >::None": { - "size": 16, - "type_class": 16384 - }, "enum2$ >::Variant1": { "size": 16, "type_class": 16384, @@ -473,64 +432,105 @@ } ] }, - "enum2$ >::Some": { + "int": { + "size": 4, + "type_class": 4, + "basic_type": 13 + }, + "int *": { + "size": 8, + "type_class": 4096 + }, + "long": { + "size": 4, + "type_class": 4, + "basic_type": 15 + }, + "long long": { + "size": 8, + "type_class": 4, + "basic_type": 17 + }, + "ref$ >": { "size": 16, "type_class": 16384, "fields": [ { - "name": "__0", - "type": "long long", + "name": "data_ptr", + "type": "int *", + "offset": 0 + }, + { + "name": "length", + "type": "unsigned long long", "offset": 8 } + ], + "generic_params": [ + "long" ] }, - "long long": { - "size": 8, - "type_class": 4, - "basic_type": 17 - }, - "std::ffi::os_str::OsString": { - "size": 32, + "ref$": { + "size": 16, "type_class": 16384, "fields": [ { - "name": "inner", - "type": "std::sys::os_str::wtf8::Buf", + "name": "data_ptr", + "type": "unsigned char *", "offset": 0 + }, + { + "name": "length", + "type": "unsigned long long", + "offset": 8 } ] }, - "std::sys::os_str::wtf8::Buf": { + "short": { + "size": 2, + "type_class": 4, + "basic_type": 11 + }, + "std::ffi::os_str::OsString": { "size": 32, "type_class": 16384, "fields": [ { "name": "inner", - "type": "alloc::wtf8::Wtf8Buf", + "type": "std::sys::os_str::wtf8::Buf", "offset": 0 } ] }, - "alloc::wtf8::Wtf8Buf": { + "std::sys::os_str::wtf8::Buf": { "size": 32, "type_class": 16384, "fields": [ { - "name": "bytes", - "type": "alloc::vec::Vec", + "name": "inner", + "type": "alloc::wtf8::Wtf8Buf", "offset": 0 - }, - { - "name": "is_known_utf8", - "type": "bool", - "offset": 24 } ] }, - "bool": { + "unsigned char": { "size": 1, "type_class": 4, - "basic_type": 21 + "basic_type": 4 + }, + "unsigned char *": { + "size": 8, + "type_class": 4096 + }, + "unsigned long long": { + "size": 8, + "type_class": 4, + "basic_type": 18 + }, + "unsigned short": { + "size": 2, + "type_class": 4, + "basic_type": 12 } } } diff --git a/tests/run-make/rustdoc-doctest-tempdir-cleanup/lib.rs b/tests/run-make/rustdoc-doctest-tempdir-cleanup/lib.rs new file mode 100644 index 0000000000000..a8585bd0bd082 --- /dev/null +++ b/tests/run-make/rustdoc-doctest-tempdir-cleanup/lib.rs @@ -0,0 +1,18 @@ +/// ``` +/// assert!(true); +/// ``` +pub fn a() {} + +/// ``` +/// let count = std::fs::read_dir(std::env::temp_dir()) +/// .unwrap() +/// .filter(|entry| { +/// entry.as_ref().unwrap().file_name().to_str().unwrap().starts_with("rustdoctest") +/// }) +/// .count(); +/// // It's 2 and not one because rustdoc creates a temp dir where it puts the `rustdoc-cfgs` +/// // file which contains the list of flags to be passed down to the doctests. It lives +/// // until all doctests have been run. +/// assert_eq!(count, 2); +/// ``` +pub fn b() {} diff --git a/tests/run-make/rustdoc-doctest-tempdir-cleanup/rmake.rs b/tests/run-make/rustdoc-doctest-tempdir-cleanup/rmake.rs new file mode 100644 index 0000000000000..789002533833c --- /dev/null +++ b/tests/run-make/rustdoc-doctest-tempdir-cleanup/rmake.rs @@ -0,0 +1,23 @@ +// This test ensures that a temporary doctest directory is removed as soon as the doctest +// was run to prevent "No space left on device". To ensure it works, we use a file with two +// doctests, run without the "merged doctests" feature (hence the "2021" edition) with +// one thread to prevent potential concurrent cleanups. +// Regression test for . + +//@ ignore-cross-compile +//@ only-linux + +use run_make_support::{is_windows, path, rfs, rustdoc}; + +fn main() { + let tmp = path("tmp"); + rfs::create_dir(&tmp); + + let mut cmd = rustdoc() + .env("TMPDIR", &tmp) + .input("lib.rs") + .edition("2021") + .arg("--test") + .arg("--test-args=--test-threads=1") + .run(); +} diff --git a/tests/rustdoc-ui/issues/issue-98690.rs b/tests/rustdoc-ui/issues/issue-98690.rs index 01708f3f64d3e..1c7eb844a272a 100644 --- a/tests/rustdoc-ui/issues/issue-98690.rs +++ b/tests/rustdoc-ui/issues/issue-98690.rs @@ -1,6 +1,8 @@ //@ compile-flags: --test --persist-doctests /../../ -Z unstable-options //@ failure-status: 101 //@ only-linux +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" #![crate_name = "foo"] diff --git a/tests/rustdoc-ui/issues/issue-98690.stderr b/tests/rustdoc-ui/issues/issue-98690.stderr deleted file mode 100644 index 47d94f99a4563..0000000000000 --- a/tests/rustdoc-ui/issues/issue-98690.stderr +++ /dev/null @@ -1 +0,0 @@ -Couldn't create directory for doctest executables: Permission denied (os error 13) diff --git a/tests/rustdoc-ui/issues/issue-98690.stdout b/tests/rustdoc-ui/issues/issue-98690.stdout new file mode 100644 index 0000000000000..970583175f18b --- /dev/null +++ b/tests/rustdoc-ui/issues/issue-98690.stdout @@ -0,0 +1,15 @@ + +running 1 test +test $DIR/issue-98690.rs - (line 9) ... FAILED + +failures: + +---- $DIR/issue-98690.rs - (line 9) stdout ---- +Couldn't create directory for doctest executables: Permission denied (os error 13) + + +failures: + $DIR/issue-98690.rs - (line 9) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/ui/assumptions_on_binders/replace_region_vars_with_placeholders.rs b/tests/ui/assumptions_on_binders/replace_region_vars_with_placeholders.rs new file mode 100644 index 0000000000000..2a8237501e039 --- /dev/null +++ b/tests/ui/assumptions_on_binders/replace_region_vars_with_placeholders.rs @@ -0,0 +1,36 @@ +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally +//@ check-pass + +// test that when rewriting alias outlives constraints we will replace +// region variables in the universe being handled, with (new) bound vars +// on the alias outlives constraint's binder. +// +// we previously did not do this and would get ambiguity which was overly +// conservative + +#![feature(test_binder_constraints)] + +trait Trait<'a> { + type Assoc; +} + +core::test_binder_constraints! { + impl Trait<'a>> { + forall<'b> + where + for<'a> >::Assoc: 'b + { + forall<> { + exists<'a> { + for<> >::Assoc: 'b + } + } expect { + for<'a> >::Assoc: 'b + } + } expect { + or { and {} } + } + } +} + +fn main() {} diff --git a/tests/ui/transmutability/alignment/align-pass.next.stderr b/tests/ui/transmutability/alignment/align-pass.next.stderr deleted file mode 100644 index 2095e97cc3915..0000000000000 --- a/tests/ui/transmutability/alignment/align-pass.next.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error[E0277]: `&[u16; 0]` cannot be safely transmuted into `&[u8; 0]` - --> $DIR/align-pass.rs:25:56 - | -LL | assert::is_maybe_transmutable::<&'static [u16; 0], &'static [u8; 0]>(); - | ^^^^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&'static [u16; 0], Assume { alignment: false, lifetimes: false, safety: true, validity: false }>` is not implemented for `&'static [u8; 0]` -note: required by a bound in `is_maybe_transmutable` - --> $DIR/align-pass.rs:13:14 - | -LL | pub fn is_maybe_transmutable() -LL | where -LL | Dst: TransmuteFrom - | |__________^ required by this bound in `is_maybe_transmutable` -help: consider removing the leading `&`-reference - | -LL - assert::is_maybe_transmutable::<&'static [u16; 0], &'static [u8; 0]>(); -LL + assert::is_maybe_transmutable::<&'static [u16; 0], [u8; 0]>(); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/alignment/align-pass.rs b/tests/ui/transmutability/alignment/align-pass.rs index fa40c3a3ade43..a87f1db089c16 100644 --- a/tests/ui/transmutability/alignment/align-pass.rs +++ b/tests/ui/transmutability/alignment/align-pass.rs @@ -1,8 +1,7 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass #![feature(transmutability)] mod assert { diff --git a/tests/ui/transmutability/references/accept_assume_lifetime_extension.next.stderr b/tests/ui/transmutability/references/accept_assume_lifetime_extension.next.stderr deleted file mode 100644 index 4c469fd2222e3..0000000000000 --- a/tests/ui/transmutability/references/accept_assume_lifetime_extension.next.stderr +++ /dev/null @@ -1,138 +0,0 @@ -error[E0277]: `&u8` cannot be safely transmuted into `&u8` - --> $DIR/accept_assume_lifetime_extension.rs:24:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `std::mem::TransmuteFrom<&u8, std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `&u8` -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `&&u8` cannot be safely transmuted into `&&u8` - --> $DIR/accept_assume_lifetime_extension.rs:32:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `std::mem::TransmuteFrom<&&u8, std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `&&u8` -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `(&u8,)` cannot be safely transmuted into `(&u8,)` - --> $DIR/accept_assume_lifetime_extension.rs:40:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `std::mem::TransmuteFrom<(&u8,), std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `(&u8,)` -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `(&u8, u8)` cannot be safely transmuted into `(&u8, u8)` - --> $DIR/accept_assume_lifetime_extension.rs:44:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `std::mem::TransmuteFrom<(&u8, u8), std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `(&u8, u8)` -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `r#struct::Struct<'_>` cannot be safely transmuted into `r#struct::Struct<'_>` - --> $DIR/accept_assume_lifetime_extension.rs:54:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | -help: the nightly-only, unstable trait `std::mem::TransmuteFrom, std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `r#struct::Struct<'_>` - --> $DIR/accept_assume_lifetime_extension.rs:51:5 - | -LL | struct Struct<'a>(&'a u8); - | ^^^^^^^^^^^^^^^^^ -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `Single<'_>` cannot be safely transmuted into `Single<'_>` - --> $DIR/accept_assume_lifetime_extension.rs:66:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | -help: the nightly-only, unstable trait `std::mem::TransmuteFrom, std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `Single<'_>` - --> $DIR/accept_assume_lifetime_extension.rs:61:5 - | -LL | enum Single<'a> { - | ^^^^^^^^^^^^^^^ -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `Multi<'_>` cannot be safely transmuted into `Multi<'_>` - --> $DIR/accept_assume_lifetime_extension.rs:76:18 - | -LL | unsafe { transmute(src) } - | ^^^^^^^^^^^^^^ unsatisfied trait bound - | -help: the nightly-only, unstable trait `std::mem::TransmuteFrom, std::mem::Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `Multi<'_>` - --> $DIR/accept_assume_lifetime_extension.rs:69:5 - | -LL | enum Multi<'a> { - | ^^^^^^^^^^^^^^ -note: required by a bound in `transmute` - --> $DIR/accept_assume_lifetime_extension.rs:15:10 - | -LL | unsafe fn transmute(src: Src) -> Dst -LL | where -LL | Dst: TransmuteFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error[E0277]: `&u8` cannot be safely transmuted into `&u8` - --> $DIR/accept_assume_lifetime_extension.rs:84:18 - | -LL | unsafe { extend_hrtb(src) } - | ^^^^^^^^^^^^---^ - | | | - | | `&u8` doesn't satisfy the trait bound - | unsatisfied trait bound - | - = help: the nightly-only, unstable trait `for<'b> std::mem::TransmuteFrom<&u8, std::mem::Assume { alignment: false, lifetimes: true, safety: false, validity: false }>` is not implemented for `&'b u8` -note: required by a bound in `extend_hrtb` - --> $DIR/accept_assume_lifetime_extension.rs:89:25 - | -LL | unsafe fn extend_hrtb<'a>(src: &'a u8) -> &'static u8 -LL | where -LL | for<'b> &'b u8: TransmuteFrom<&'a u8, { Assume::LIFETIMES }>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extend_hrtb` - -error: aborting due to 8 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs b/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs index 86feb3c40eb7d..fea6675d751c9 100644 --- a/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs +++ b/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs @@ -1,8 +1,7 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass //! Accept lifetime extensions with `Assume::LIFETIMES`. diff --git a/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.next.stderr b/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.next.stderr deleted file mode 100644 index 32156298a1f6f..0000000000000 --- a/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.next.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error[E0277]: `&A` cannot be safely transmuted into `&B` - --> $DIR/recursive-wrapper-types-bit-compatible.rs:27:49 - | -LL | assert::is_maybe_transmutable::<&'static A, &'static B>(); - | ^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&'static A, Assume { alignment: true, lifetimes: false, safety: true, validity: false }>` is not implemented for `&'static B` -note: required by a bound in `is_maybe_transmutable` - --> $DIR/recursive-wrapper-types-bit-compatible.rs:13:14 - | -LL | pub fn is_maybe_transmutable() -LL | where -LL | Dst: TransmuteFrom - | |__________^ required by this bound in `is_maybe_transmutable` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs b/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs index f1bb503549968..e3fad55910e92 100644 --- a/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs +++ b/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs @@ -1,8 +1,7 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass #![feature(transmutability)] mod assert { diff --git a/tests/ui/transmutability/references/recursive-wrapper-types.next.stderr b/tests/ui/transmutability/references/recursive-wrapper-types.next.stderr deleted file mode 100644 index 8ea346d7c08b5..0000000000000 --- a/tests/ui/transmutability/references/recursive-wrapper-types.next.stderr +++ /dev/null @@ -1,45 +0,0 @@ -error[E0277]: `&A` cannot be safely transmuted into `&B` - --> $DIR/recursive-wrapper-types.rs:27:49 - | -LL | assert::is_maybe_transmutable::<&'static A, &'static B>(); - | ^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&'static A, Assume { alignment: true, lifetimes: false, safety: true, validity: false }>` is not implemented for `&'static B` -note: required by a bound in `is_maybe_transmutable` - --> $DIR/recursive-wrapper-types.rs:13:14 - | -LL | pub fn is_maybe_transmutable() -LL | where -LL | Dst: TransmuteFrom - | |__________^ required by this bound in `is_maybe_transmutable` - -error[E0277]: `&B` cannot be safely transmuted into `&A` - --> $DIR/recursive-wrapper-types.rs:28:49 - | -LL | assert::is_maybe_transmutable::<&'static B, &'static A>(); - | ^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&'static B, Assume { alignment: true, lifetimes: false, safety: true, validity: false }>` is not implemented for `&'static A` -note: required by a bound in `is_maybe_transmutable` - --> $DIR/recursive-wrapper-types.rs:13:14 - | -LL | pub fn is_maybe_transmutable() -LL | where -LL | Dst: TransmuteFrom - | |__________^ required by this bound in `is_maybe_transmutable` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/references/recursive-wrapper-types.rs b/tests/ui/transmutability/references/recursive-wrapper-types.rs index 7ff458aa2512b..b1626d21e9731 100644 --- a/tests/ui/transmutability/references/recursive-wrapper-types.rs +++ b/tests/ui/transmutability/references/recursive-wrapper-types.rs @@ -1,8 +1,7 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass #![feature(transmutability)] mod assert { diff --git a/tests/ui/transmutability/references/u8-to-unit.next.stderr b/tests/ui/transmutability/references/u8-to-unit.next.stderr deleted file mode 100644 index 0897833202826..0000000000000 --- a/tests/ui/transmutability/references/u8-to-unit.next.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error[E0277]: `&u8` cannot be safely transmuted into `&Unit` - --> $DIR/u8-to-unit.rs:26:50 - | -LL | assert::is_maybe_transmutable::<&'static u8, &'static Unit>(); - | ^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&'static u8, Assume { alignment: false, lifetimes: true, safety: true, validity: false }>` is not implemented for `&'static Unit` -note: required by a bound in `is_maybe_transmutable` - --> $DIR/u8-to-unit.rs:13:14 - | -LL | pub fn is_maybe_transmutable() -LL | where -LL | Dst: TransmuteFrom - | |__________^ required by this bound in `is_maybe_transmutable` -help: consider removing the leading `&`-reference - | -LL - assert::is_maybe_transmutable::<&'static u8, &'static Unit>(); -LL + assert::is_maybe_transmutable::<&'static u8, Unit>(); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/references/u8-to-unit.rs b/tests/ui/transmutability/references/u8-to-unit.rs index df124cafda3d6..09f1a5d64bb45 100644 --- a/tests/ui/transmutability/references/u8-to-unit.rs +++ b/tests/ui/transmutability/references/u8-to-unit.rs @@ -1,8 +1,7 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass #![feature(transmutability)] mod assert { diff --git a/tests/ui/transmutability/references/unit-to-itself.next.stderr b/tests/ui/transmutability/references/unit-to-itself.next.stderr deleted file mode 100644 index 4a15b3b3d5c30..0000000000000 --- a/tests/ui/transmutability/references/unit-to-itself.next.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error[E0277]: `&Unit` cannot be safely transmuted into `&Unit` - --> $DIR/unit-to-itself.rs:26:52 - | -LL | assert::is_maybe_transmutable::<&'static Unit, &'static Unit>(); - | ^^^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&'static Unit, Assume { alignment: true, lifetimes: false, safety: true, validity: false }>` is not implemented for `&'static Unit` -note: required by a bound in `is_maybe_transmutable` - --> $DIR/unit-to-itself.rs:13:14 - | -LL | pub fn is_maybe_transmutable() -LL | where -LL | Dst: TransmuteFrom - | |__________^ required by this bound in `is_maybe_transmutable` -help: consider removing the leading `&`-reference - | -LL - assert::is_maybe_transmutable::<&'static Unit, &'static Unit>(); -LL + assert::is_maybe_transmutable::<&'static Unit, Unit>(); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/references/unit-to-itself.rs b/tests/ui/transmutability/references/unit-to-itself.rs index 87d001222f96e..5e001432caec9 100644 --- a/tests/ui/transmutability/references/unit-to-itself.rs +++ b/tests/ui/transmutability/references/unit-to-itself.rs @@ -1,8 +1,7 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass #![feature(transmutability)] mod assert { diff --git a/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.next.stderr b/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.next.stderr deleted file mode 100644 index 2cbaa71262cea..0000000000000 --- a/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.next.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0277]: `&mut Src` cannot be safely transmuted into `&mut u8` - --> $DIR/should_accept_if_ref_src_has_safety_invariant.rs:29:41 - | -LL | assert::is_transmutable::<&mut Src, &mut Dst>(); - | ^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `TransmuteFrom<&mut Src, Assume { alignment: false, lifetimes: false, safety: true, validity: false }>` is not implemented for `&mut u8` -note: required by a bound in `is_transmutable` - --> $DIR/should_accept_if_ref_src_has_safety_invariant.rs:19:14 - | -LL | pub fn is_transmutable() -LL | where -LL | Dst: TransmuteFrom - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.rs b/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.rs index 32d70c3298eda..ed89ad3c3a679 100644 --- a/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.rs +++ b/tests/ui/transmutability/safety/assume/should_accept_if_ref_src_has_safety_invariant.rs @@ -1,8 +1,7 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass //! When safety is assumed, a transmutation over exclusive references should be //! accepted if the source type potentially carries safety invariants. diff --git a/tests/ui/transmutability/transmute-higher-ranked.next.stderr b/tests/ui/transmutability/transmute-higher-ranked.next.stderr deleted file mode 100644 index 9d86559f62bcc..0000000000000 --- a/tests/ui/transmutability/transmute-higher-ranked.next.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0277]: `&&u32` cannot be safely transmuted into `&&i32` - --> $DIR/transmute-higher-ranked.rs:21:5 - | -LL | transmute(); - | ^^^^^^^^^^^ unsatisfied trait bound - | - = help: the nightly-only, unstable trait `for<'a> TransmuteFrom<&'a &'a u32, Assume { alignment: false, lifetimes: false, safety: false, validity: false }>` is not implemented for `&'a &'a i32` -note: required by a bound in `transmute` - --> $DIR/transmute-higher-ranked.rs:16:26 - | -LL | pub fn transmute() -LL | where -LL | for<'a> &'a &'a i32: TransmuteFrom<&'a &'a u32>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `transmute` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/transmute-higher-ranked.rs b/tests/ui/transmutability/transmute-higher-ranked.rs index fc0753942906a..43c417daa2637 100644 --- a/tests/ui/transmutability/transmute-higher-ranked.rs +++ b/tests/ui/transmutability/transmute-higher-ranked.rs @@ -4,8 +4,7 @@ //@ revisions: old next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] known-bug: trait-system-refactor-initiative#152 -//@[old] check-pass +//@ check-pass #![feature(transmutability)] diff --git a/tests/ui/transmutability/vec-recycle-next-solver.rs b/tests/ui/transmutability/vec-recycle-next-solver.rs new file mode 100644 index 0000000000000..ba68a31cea624 --- /dev/null +++ b/tests/ui/transmutability/vec-recycle-next-solver.rs @@ -0,0 +1,16 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver +//@ check-pass + +#![feature(vec_recycle, transmutability)] + +fn main() { + let a: Vec = vec![0; 100]; + let capacity = a.capacity(); + let addr = a.as_ptr().addr(); + let b: Vec = a.recycle(); + assert_eq!(b.len(), 0); + assert_eq!(b.capacity(), capacity); + assert_eq!(b.as_ptr().addr(), addr); +} diff --git a/tests/ui/transmutability/with-condition-nested-goals.current.stderr b/tests/ui/transmutability/with-condition-nested-goals.current.stderr new file mode 100644 index 0000000000000..27fbf8e92e811 --- /dev/null +++ b/tests/ui/transmutability/with-condition-nested-goals.current.stderr @@ -0,0 +1,11 @@ +error[E0277]: `i32` cannot be safely transmuted into `[NewType; 4]` + --> $DIR/with-condition-nested-goals.rs:16:74 + | +LL | let bytes: &[NewType; 4] = unsafe { TransmuteFrom::<&i32>::transmute(&1i32) }; + | -------------------------------- ^^^^^ `[NewType; 4]` may carry safety invariants + | | + | required by a bound introduced by this call + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/with-condition-nested-goals.next.stderr b/tests/ui/transmutability/with-condition-nested-goals.next.stderr new file mode 100644 index 0000000000000..dbb0747c3ff63 --- /dev/null +++ b/tests/ui/transmutability/with-condition-nested-goals.next.stderr @@ -0,0 +1,13 @@ +error[E0277]: `&i32` cannot be safely transmuted into `&[NewType; 4]` + --> $DIR/with-condition-nested-goals.rs:16:74 + | +LL | let bytes: &[NewType; 4] = unsafe { TransmuteFrom::<&i32>::transmute(&1i32) }; + | -------------------------------- ^^^^^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | + = help: the nightly-only, unstable trait `TransmuteFrom<&i32, Assume { alignment: false, lifetimes: false, safety: false, validity: false }>` is not implemented for `&[NewType; 4]` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/with-condition-nested-goals.rs b/tests/ui/transmutability/with-condition-nested-goals.rs new file mode 100644 index 0000000000000..1561a0a20fe55 --- /dev/null +++ b/tests/ui/transmutability/with-condition-nested-goals.rs @@ -0,0 +1,19 @@ +//! This UI test ensures that transmutability check tests nested goals. +//! If the nested goals regarding the NewType implementing the trait +//! was not being tested, this transmute call would compile. +// +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver +//@ check-fail + +#![feature(transmutability)] +use std::mem::TransmuteFrom; + +struct NewType(u8); + +fn main() { + let bytes: &[NewType; 4] = unsafe { TransmuteFrom::<&i32>::transmute(&1i32) }; + //[current]~^ ERROR `i32` cannot be safely transmuted into `[NewType; 4]` + //[next]~^^ ERROR `&i32` cannot be safely transmuted into `&[NewType; 4]` +} diff --git a/tests/ui/transmute/ambiguity-in-closure-arg.stderr b/tests/ui/transmute/ambiguity-in-closure-arg.current.stderr similarity index 87% rename from tests/ui/transmute/ambiguity-in-closure-arg.stderr rename to tests/ui/transmute/ambiguity-in-closure-arg.current.stderr index 24a10a6d210b4..3a38bb35ac793 100644 --- a/tests/ui/transmute/ambiguity-in-closure-arg.stderr +++ b/tests/ui/transmute/ambiguity-in-closure-arg.current.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/ambiguity-in-closure-arg.rs:6:44 + --> $DIR/ambiguity-in-closure-arg.rs:11:44 | LL | unsafe { std::mem::transmute::<_, ()>(|o_b: Unconstrained<_>| {}) }; | ^^^^^^^^^^^^^^^^^^^^^ cannot infer type diff --git a/tests/ui/transmute/ambiguity-in-closure-arg.next.stderr b/tests/ui/transmute/ambiguity-in-closure-arg.next.stderr new file mode 100644 index 0000000000000..3a38bb35ac793 --- /dev/null +++ b/tests/ui/transmute/ambiguity-in-closure-arg.next.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/ambiguity-in-closure-arg.rs:11:44 + | +LL | unsafe { std::mem::transmute::<_, ()>(|o_b: Unconstrained<_>| {}) }; + | ^^^^^^^^^^^^^^^^^^^^^ cannot infer type + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/transmute/ambiguity-in-closure-arg.rs b/tests/ui/transmute/ambiguity-in-closure-arg.rs index 4c2d1ce2ad463..1926fb15f7b2d 100644 --- a/tests/ui/transmute/ambiguity-in-closure-arg.rs +++ b/tests/ui/transmute/ambiguity-in-closure-arg.rs @@ -1,4 +1,9 @@ -// Minimized test for . +//! Minimized test for . + +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver +//@ check-fail struct Unconstrained(T); diff --git a/tests/ui/transmute/diverging-fn-transmute.stderr b/tests/ui/transmute/diverging-fn-transmute.current.stderr similarity index 92% rename from tests/ui/transmute/diverging-fn-transmute.stderr rename to tests/ui/transmute/diverging-fn-transmute.current.stderr index b9aeae7ed62b1..37b13e17a70e8 100644 --- a/tests/ui/transmute/diverging-fn-transmute.stderr +++ b/tests/ui/transmute/diverging-fn-transmute.current.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/diverging-fn-transmute.rs:5:9 + --> $DIR/diverging-fn-transmute.rs:7:9 | LL | fn assert_sizeof() -> ! { | - expected `!` because of return type diff --git a/tests/ui/transmute/diverging-fn-transmute.next.stderr b/tests/ui/transmute/diverging-fn-transmute.next.stderr new file mode 100644 index 0000000000000..37b13e17a70e8 --- /dev/null +++ b/tests/ui/transmute/diverging-fn-transmute.next.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/diverging-fn-transmute.rs:7:9 + | +LL | fn assert_sizeof() -> ! { + | - expected `!` because of return type +LL | unsafe { +LL | ::std::mem::transmute::(panic!()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `!`, found `[u8; 8]` + | + = note: expected type `!` + found array `[u8; 8]` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/transmute/diverging-fn-transmute.rs b/tests/ui/transmute/diverging-fn-transmute.rs index aca82037a0c2c..aedd3c53514c2 100644 --- a/tests/ui/transmute/diverging-fn-transmute.rs +++ b/tests/ui/transmute/diverging-fn-transmute.rs @@ -1,5 +1,7 @@ //! Regression test for issue #35849: transmute with panic in diverging function - +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver fn assert_sizeof() -> ! { unsafe { ::std::mem::transmute::(panic!()) diff --git a/tests/ui/transmute/lifetimes.rs b/tests/ui/transmute/lifetimes.rs index 0114e6205bb38..926d2129821f7 100644 --- a/tests/ui/transmute/lifetimes.rs +++ b/tests/ui/transmute/lifetimes.rs @@ -1,5 +1,7 @@ //@ check-pass - +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver use std::ptr::NonNull; struct Foo<'a, T: ?Sized>(&'a (), NonNull); diff --git a/tests/ui/transmute/main.stderr b/tests/ui/transmute/main.current.stderr similarity index 92% rename from tests/ui/transmute/main.stderr rename to tests/ui/transmute/main.current.stderr index 6cb0d7f67e085..2a8a131a5e53c 100644 --- a/tests/ui/transmute/main.stderr +++ b/tests/ui/transmute/main.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/main.rs:17:17 + --> $DIR/main.rs:19:17 | LL | let x: u8 = transmute(10u16); | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let x: u8 = transmute(10u16); = note: target type: `u8` (8 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/main.rs:21:17 + --> $DIR/main.rs:23:17 | LL | let x: u8 = transmute("test"); | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let x: u8 = transmute("test"); = note: target type: `u8` (8 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/main.rs:26:18 + --> $DIR/main.rs:28:18 | LL | let x: Foo = transmute(10); | ^^^^^^^^^ diff --git a/tests/ui/transmute/main.next.stderr b/tests/ui/transmute/main.next.stderr new file mode 100644 index 0000000000000..2a8a131a5e53c --- /dev/null +++ b/tests/ui/transmute/main.next.stderr @@ -0,0 +1,30 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/main.rs:19:17 + | +LL | let x: u8 = transmute(10u16); + | ^^^^^^^^^ + | + = note: source type: `u16` (16 bits) + = note: target type: `u8` (8 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/main.rs:23:17 + | +LL | let x: u8 = transmute("test"); + | ^^^^^^^^^ + | + = note: source type: `&str` ($STR bits) + = note: target type: `u8` (8 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/main.rs:28:18 + | +LL | let x: Foo = transmute(10); + | ^^^^^^^^^ + | + = note: source type: `i32` (32 bits) + = note: target type: `Foo` (0 bits) + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/main.rs b/tests/ui/transmute/main.rs index e4a5f9103cd79..941944f749466 100644 --- a/tests/ui/transmute/main.rs +++ b/tests/ui/transmute/main.rs @@ -1,6 +1,8 @@ //@ normalize-stderr-32bit: "`&str` \(64 bits\)" -> "`&str` ($$STR bits)" //@ normalize-stderr-64bit: "`&str` \(128 bits\)" -> "`&str` ($$STR bits)" - +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver use std::mem::transmute; pub trait TypeConstructor<'a> { diff --git a/tests/ui/transmute/recursive-size-skeleton.stderr b/tests/ui/transmute/recursive-size-skeleton.current.stderr similarity index 88% rename from tests/ui/transmute/recursive-size-skeleton.stderr rename to tests/ui/transmute/recursive-size-skeleton.current.stderr index 05bdf6ae49905..a1a61480b8e26 100644 --- a/tests/ui/transmute/recursive-size-skeleton.stderr +++ b/tests/ui/transmute/recursive-size-skeleton.current.stderr @@ -1,5 +1,5 @@ error: reached the recursion limit while computing the size of `Thing` - --> $DIR/recursive-size-skeleton.rs:20:27 + --> $DIR/recursive-size-skeleton.rs:22:27 | LL | let _: i32 = unsafe { transmute(value) }; | ^^^^^^^^^ @@ -7,7 +7,7 @@ LL | let _: i32 = unsafe { transmute(value) }; = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/recursive-size-skeleton.rs:20:27 + --> $DIR/recursive-size-skeleton.rs:22:27 | LL | let _: i32 = unsafe { transmute(value) }; | ^^^^^^^^^ diff --git a/tests/ui/transmute/recursive-size-skeleton.next.stderr b/tests/ui/transmute/recursive-size-skeleton.next.stderr new file mode 100644 index 0000000000000..a1a61480b8e26 --- /dev/null +++ b/tests/ui/transmute/recursive-size-skeleton.next.stderr @@ -0,0 +1,20 @@ +error: reached the recursion limit while computing the size of `Thing` + --> $DIR/recursive-size-skeleton.rs:22:27 + | +LL | let _: i32 = unsafe { transmute(value) }; + | ^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/recursive-size-skeleton.rs:22:27 + | +LL | let _: i32 = unsafe { transmute(value) }; + | ^^^^^^^^^ + | + = note: source type: `Thing` (the type has an unknown layout) + = note: target type: `i32` (32 bits) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/recursive-size-skeleton.rs b/tests/ui/transmute/recursive-size-skeleton.rs index b2395a8f5aa4b..138bc0eb34324 100644 --- a/tests/ui/transmute/recursive-size-skeleton.rs +++ b/tests/ui/transmute/recursive-size-skeleton.rs @@ -3,7 +3,9 @@ // through a normalizing type alias used to recurse without bound and // blow the stack. We now bail out via the recursion limit and emit a // regular error instead of ICE-ing. - +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver use std::mem::transmute; trait Trait { diff --git a/tests/ui/transmute/transmute-array-to-scalar.rs b/tests/ui/transmute/transmute-array-to-scalar.rs index cd6dbb040c8bb..c80d13fa7db03 100644 --- a/tests/ui/transmute/transmute-array-to-scalar.rs +++ b/tests/ui/transmute/transmute-array-to-scalar.rs @@ -1,7 +1,9 @@ //! Verify transmuting from a single-element array to a scalar is allowed. //! //! Regression test: - +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ run-pass pub fn main() { diff --git a/tests/ui/transmute/transmute-associated-type-to-slice.stderr b/tests/ui/transmute/transmute-associated-type-to-slice.current.stderr similarity index 87% rename from tests/ui/transmute/transmute-associated-type-to-slice.stderr rename to tests/ui/transmute/transmute-associated-type-to-slice.current.stderr index ada3153165e57..ac59b0c70be65 100644 --- a/tests/ui/transmute/transmute-associated-type-to-slice.stderr +++ b/tests/ui/transmute/transmute-associated-type-to-slice.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-associated-type-to-slice.rs:13:14 + --> $DIR/transmute-associated-type-to-slice.rs:15:14 | LL | unsafe { std::mem::transmute(a) } | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-associated-type-to-slice.next.stderr b/tests/ui/transmute/transmute-associated-type-to-slice.next.stderr new file mode 100644 index 0000000000000..ac59b0c70be65 --- /dev/null +++ b/tests/ui/transmute/transmute-associated-type-to-slice.next.stderr @@ -0,0 +1,12 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-associated-type-to-slice.rs:15:14 + | +LL | unsafe { std::mem::transmute(a) } + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `&ArrayPeano` (N bits) + = note: target type: `&[T]` (N bits) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-associated-type-to-slice.rs b/tests/ui/transmute/transmute-associated-type-to-slice.rs index 9521a944391cc..5b56c541143a8 100644 --- a/tests/ui/transmute/transmute-associated-type-to-slice.rs +++ b/tests/ui/transmute/transmute-associated-type-to-slice.rs @@ -1,6 +1,8 @@ //! regression test for //@ normalize-stderr: "\d+ bits" -> "N bits" - +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver trait MyTrait { type MyType; } diff --git a/tests/ui/transmute/transmute-bool-u8.rs b/tests/ui/transmute/transmute-bool-u8.rs index 502683b6d1366..9bd4ac212d399 100644 --- a/tests/ui/transmute/transmute-bool-u8.rs +++ b/tests/ui/transmute/transmute-bool-u8.rs @@ -1,4 +1,7 @@ //! regression test for +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ build-pass #![allow(unnecessary_transmutes)] use std::mem::transmute; diff --git a/tests/ui/transmute/transmute-different-sizes.stderr b/tests/ui/transmute/transmute-different-sizes.current.stderr similarity index 86% rename from tests/ui/transmute/transmute-different-sizes.stderr rename to tests/ui/transmute/transmute-different-sizes.current.stderr index fe611f7ba18d1..d8b1898b25e91 100644 --- a/tests/ui/transmute/transmute-different-sizes.stderr +++ b/tests/ui/transmute/transmute-different-sizes.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:11:17 + --> $DIR/transmute-different-sizes.rs:13:17 | LL | let _: i8 = transmute(16i16); | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _: i8 = transmute(16i16); = note: target type: `i8` (N bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:16:17 + --> $DIR/transmute-different-sizes.rs:18:17 | LL | let _: i8 = transmute(x); | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let _: i8 = transmute(x); = note: target type: `i8` (N bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:27:5 + --> $DIR/transmute-different-sizes.rs:29:5 | LL | transmute(x) | ^^^^^^^^^ @@ -26,7 +26,7 @@ LL | transmute(x) = note: target type: `::Output` (this type does not have a fixed size) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:38:5 + --> $DIR/transmute-different-sizes.rs:40:5 | LL | transmute(from) | ^^^^^^^^^ @@ -35,7 +35,7 @@ LL | transmute(from) = note: target type: `PtrAndOverAlignZST` (size can vary because of ::Metadata) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:47:5 + --> $DIR/transmute-different-sizes.rs:49:5 | LL | std::mem::transmute(from) | ^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | std::mem::transmute(from) = note: target type: `PtrAndEmptyArray` (size can vary because of ::Metadata) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:55:14 + --> $DIR/transmute-different-sizes.rs:57:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ @@ -53,7 +53,7 @@ LL | unsafe { transmute(x) } = note: target type: `*const T` (pointer to `T`) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:66:14 + --> $DIR/transmute-different-sizes.rs:68:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ @@ -62,7 +62,7 @@ LL | unsafe { transmute(x) } = note: target type: `NotANewtype<&T>` (size can vary because of ::Metadata) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-different-sizes.rs:79:14 + --> $DIR/transmute-different-sizes.rs:81:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-different-sizes.next.stderr b/tests/ui/transmute/transmute-different-sizes.next.stderr new file mode 100644 index 0000000000000..d8b1898b25e91 --- /dev/null +++ b/tests/ui/transmute/transmute-different-sizes.next.stderr @@ -0,0 +1,75 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:13:17 + | +LL | let _: i8 = transmute(16i16); + | ^^^^^^^^^ + | + = note: source type: `i16` (N bits) + = note: target type: `i8` (N bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:18:17 + | +LL | let _: i8 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `&T` (N bits) + = note: target type: `i8` (N bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:29:5 + | +LL | transmute(x) + | ^^^^^^^^^ + | + = note: source type: `u16` (N bits) + = note: target type: `::Output` (this type does not have a fixed size) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:40:5 + | +LL | transmute(from) + | ^^^^^^^^^ + | + = note: source type: `*mut T` (pointer to `T`) + = note: target type: `PtrAndOverAlignZST` (size can vary because of ::Metadata) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:49:5 + | +LL | std::mem::transmute(from) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `*mut T` (pointer to `T`) + = note: target type: `PtrAndEmptyArray` (size can vary because of ::Metadata) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:57:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `OverAlignWrap<*const T>` (size can vary because of ::Metadata) + = note: target type: `*const T` (pointer to `T`) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:68:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (pointer to `T`) + = note: target type: `NotANewtype<&T>` (size can vary because of ::Metadata) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:81:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (pointer to `T`) + = note: target type: `NotNullPointerOptimized<&T>` (size can vary because of ::Metadata) + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-different-sizes.rs b/tests/ui/transmute/transmute-different-sizes.rs index 4f1f758677145..c269b571d9e31 100644 --- a/tests/ui/transmute/transmute-different-sizes.rs +++ b/tests/ui/transmute/transmute-different-sizes.rs @@ -1,5 +1,7 @@ //@ normalize-stderr: "\d+ bits" -> "N bits" - +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver // Tests that `transmute` cannot be called on types of different size. #![allow(warnings)] diff --git a/tests/ui/transmute/transmute-fat-pointers.stderr b/tests/ui/transmute/transmute-fat-pointers.current.stderr similarity index 86% rename from tests/ui/transmute/transmute-fat-pointers.stderr rename to tests/ui/transmute/transmute-fat-pointers.current.stderr index 56a4f7f33d152..e94ff321e9197 100644 --- a/tests/ui/transmute/transmute-fat-pointers.stderr +++ b/tests/ui/transmute/transmute-fat-pointers.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-fat-pointers.rs:11:14 + --> $DIR/transmute-fat-pointers.rs:13:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | unsafe { transmute(x) } = note: target type: `&U` (pointer to `U`) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-fat-pointers.rs:15:14 + --> $DIR/transmute-fat-pointers.rs:17:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | unsafe { transmute(x) } = note: target type: `&U` (pointer to `U`) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-fat-pointers.rs:27:14 + --> $DIR/transmute-fat-pointers.rs:29:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ @@ -26,7 +26,7 @@ LL | unsafe { transmute(x) } = note: target type: `&U` (N bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-fat-pointers.rs:31:14 + --> $DIR/transmute-fat-pointers.rs:33:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-fat-pointers.next.stderr b/tests/ui/transmute/transmute-fat-pointers.next.stderr new file mode 100644 index 0000000000000..e94ff321e9197 --- /dev/null +++ b/tests/ui/transmute/transmute-fat-pointers.next.stderr @@ -0,0 +1,39 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fat-pointers.rs:13:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&[T]` (N bits) + = note: target type: `&U` (pointer to `U`) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fat-pointers.rs:17:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (pointer to `T`) + = note: target type: `&U` (pointer to `U`) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fat-pointers.rs:29:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (pointer to `T`) + = note: target type: `&U` (N bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fat-pointers.rs:33:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (N bits) + = note: target type: `&U` (pointer to `U`) + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-fat-pointers.rs b/tests/ui/transmute/transmute-fat-pointers.rs index c03d0d34feb1b..eba750d9229a6 100644 --- a/tests/ui/transmute/transmute-fat-pointers.rs +++ b/tests/ui/transmute/transmute-fat-pointers.rs @@ -1,5 +1,7 @@ //@ normalize-stderr: "\d+ bits" -> "N bits" - +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver // Tests that are conservative around thin/fat pointer mismatches. #![allow(dead_code)] diff --git a/tests/ui/transmute/transmute-from-fn-item-types-error.stderr b/tests/ui/transmute/transmute-from-fn-item-types-error.current.stderr similarity index 84% rename from tests/ui/transmute/transmute-from-fn-item-types-error.stderr rename to tests/ui/transmute/transmute-from-fn-item-types-error.current.stderr index aefe3fb8e25fd..c77725b1c4eb0 100644 --- a/tests/ui/transmute/transmute-from-fn-item-types-error.stderr +++ b/tests/ui/transmute/transmute-from-fn-item-types-error.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-from-fn-item-types-error.rs:4:13 + --> $DIR/transmute-from-fn-item-types-error.rs:7:13 | LL | let i = mem::transmute(bar); | ^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let i = mem::transmute(bar); = note: target type: `i8` (8 bits) error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:8:13 + --> $DIR/transmute-from-fn-item-types-error.rs:11:13 | LL | let p = mem::transmute(foo); | ^^^^^^^^^^^^^^ @@ -18,7 +18,7 @@ LL | let p = mem::transmute(foo); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:12:14 + --> $DIR/transmute-from-fn-item-types-error.rs:15:14 | LL | let of = mem::transmute(main); | ^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let of = mem::transmute(main); = help: cast with `as` to a pointer instead error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-from-fn-item-types-error.rs:21:5 + --> $DIR/transmute-from-fn-item-types-error.rs:24:5 | LL | mem::transmute::<_, u8>(main); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | mem::transmute::<_, u8>(main); = note: target type: `u8` (8 bits) error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:25:5 + --> $DIR/transmute-from-fn-item-types-error.rs:28:5 | LL | mem::transmute::<_, *mut ()>(foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | mem::transmute::<_, *mut ()>(foo); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:29:5 + --> $DIR/transmute-from-fn-item-types-error.rs:32:5 | LL | mem::transmute::<_, fn()>(bar); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | mem::transmute::<_, fn()>(bar); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:38:5 + --> $DIR/transmute-from-fn-item-types-error.rs:41:5 | LL | mem::transmute::<_, *mut ()>(Some(foo)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -67,7 +67,7 @@ LL | mem::transmute::<_, *mut ()>(Some(foo)); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:42:5 + --> $DIR/transmute-from-fn-item-types-error.rs:45:5 | LL | mem::transmute::<_, fn()>(Some(bar)); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | mem::transmute::<_, fn()>(Some(bar)); = help: cast with `as` to a pointer instead error[E0591]: can't transmute zero-sized type - --> $DIR/transmute-from-fn-item-types-error.rs:46:5 + --> $DIR/transmute-from-fn-item-types-error.rs:49:5 | LL | mem::transmute::<_, Option>(Some(baz)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-from-fn-item-types-error.next.stderr b/tests/ui/transmute/transmute-from-fn-item-types-error.next.stderr new file mode 100644 index 0000000000000..c77725b1c4eb0 --- /dev/null +++ b/tests/ui/transmute/transmute-from-fn-item-types-error.next.stderr @@ -0,0 +1,92 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-from-fn-item-types-error.rs:7:13 + | +LL | let i = mem::transmute(bar); + | ^^^^^^^^^^^^^^ + | + = note: source type: `unsafe fn() {bar}` (0 bits) + = note: target type: `i8` (8 bits) + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:11:13 + | +LL | let p = mem::transmute(foo); + | ^^^^^^^^^^^^^^ + | + = note: source type: unsafe fn() -> (i8, *const (), Option) {foo} + = note: target type: *const () + = help: cast with `as` to a pointer instead + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:15:14 + | +LL | let of = mem::transmute(main); + | ^^^^^^^^^^^^^^ + | + = note: source type: fn() {main} + = note: target type: Option + = help: cast with `as` to a pointer instead + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-from-fn-item-types-error.rs:24:5 + | +LL | mem::transmute::<_, u8>(main); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `fn() {main}` (0 bits) + = note: target type: `u8` (8 bits) + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:28:5 + | +LL | mem::transmute::<_, *mut ()>(foo); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: unsafe fn() -> (i8, *const (), Option) {foo} + = note: target type: *mut () + = help: cast with `as` to a pointer instead + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:32:5 + | +LL | mem::transmute::<_, fn()>(bar); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: unsafe fn() {bar} + = note: target type: fn() + = help: cast with `as` to a pointer instead + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:41:5 + | +LL | mem::transmute::<_, *mut ()>(Some(foo)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: unsafe fn() -> (i8, *const (), Option) {foo} + = note: target type: *mut () + = help: cast with `as` to a pointer instead + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:45:5 + | +LL | mem::transmute::<_, fn()>(Some(bar)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: unsafe fn() {bar} + = note: target type: fn() + = help: cast with `as` to a pointer instead + +error[E0591]: can't transmute zero-sized type + --> $DIR/transmute-from-fn-item-types-error.rs:49:5 + | +LL | mem::transmute::<_, Option>(Some(baz)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: unsafe fn() {baz} + = note: target type: Option + = help: cast with `as` to a pointer instead + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0512, E0591. +For more information about an error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-from-fn-item-types-error.rs b/tests/ui/transmute/transmute-from-fn-item-types-error.rs index f858a199e48e3..c1161305406d1 100644 --- a/tests/ui/transmute/transmute-from-fn-item-types-error.rs +++ b/tests/ui/transmute/transmute-from-fn-item-types-error.rs @@ -1,3 +1,6 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver use std::mem; unsafe fn foo() -> (i8, *const (), Option) { diff --git a/tests/ui/transmute/transmute-impl.stderr b/tests/ui/transmute/transmute-impl.current.stderr similarity index 91% rename from tests/ui/transmute/transmute-impl.stderr rename to tests/ui/transmute/transmute-impl.current.stderr index 6ed1cd6cec455..598f43cddced0 100644 --- a/tests/ui/transmute/transmute-impl.stderr +++ b/tests/ui/transmute/transmute-impl.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-impl.rs:21:18 + --> $DIR/transmute-impl.rs:23:18 | LL | unsafe { transmute(x) } | ^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-impl.next.stderr b/tests/ui/transmute/transmute-impl.next.stderr new file mode 100644 index 0000000000000..598f43cddced0 --- /dev/null +++ b/tests/ui/transmute/transmute-impl.next.stderr @@ -0,0 +1,12 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-impl.rs:23:18 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (pointer to `T`) + = note: target type: `&isize` (N bits) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-impl.rs b/tests/ui/transmute/transmute-impl.rs index 84951a0ee6035..1e5cf6509f6ba 100644 --- a/tests/ui/transmute/transmute-impl.rs +++ b/tests/ui/transmute/transmute-impl.rs @@ -1,5 +1,7 @@ //@ normalize-stderr: "\d+ bits" -> "N bits" - +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver // Tests that are conservative around thin/fat pointer mismatches. #![allow(dead_code)] diff --git a/tests/ui/transmute/transmute-imut-to-mut.stderr b/tests/ui/transmute/transmute-imut-to-mut.current.stderr similarity index 89% rename from tests/ui/transmute/transmute-imut-to-mut.stderr rename to tests/ui/transmute/transmute-imut-to-mut.current.stderr index d37050fa58af6..265426641d57e 100644 --- a/tests/ui/transmute/transmute-imut-to-mut.stderr +++ b/tests/ui/transmute/transmute-imut-to-mut.current.stderr @@ -1,5 +1,5 @@ error: transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell - --> $DIR/transmute-imut-to-mut.rs:6:32 + --> $DIR/transmute-imut-to-mut.rs:8:32 | LL | let _a: &mut u8 = unsafe { transmute(&1u8) }; | ^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-imut-to-mut.next.stderr b/tests/ui/transmute/transmute-imut-to-mut.next.stderr new file mode 100644 index 0000000000000..265426641d57e --- /dev/null +++ b/tests/ui/transmute/transmute-imut-to-mut.next.stderr @@ -0,0 +1,10 @@ +error: transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell + --> $DIR/transmute-imut-to-mut.rs:8:32 + | +LL | let _a: &mut u8 = unsafe { transmute(&1u8) }; + | ^^^^^^^^^ + | + = note: `#[deny(mutable_transmutes)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/transmute/transmute-imut-to-mut.rs b/tests/ui/transmute/transmute-imut-to-mut.rs index 9f3f76c1ef3e0..85c067366e549 100644 --- a/tests/ui/transmute/transmute-imut-to-mut.rs +++ b/tests/ui/transmute/transmute-imut-to-mut.rs @@ -1,5 +1,7 @@ // Tests that transmuting from &T to &mut T is Undefined Behavior. - +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver use std::mem::transmute; fn main() { diff --git a/tests/ui/transmute/transmute-in-async-fn.stderr b/tests/ui/transmute/transmute-in-async-fn.current.stderr similarity index 89% rename from tests/ui/transmute/transmute-in-async-fn.stderr rename to tests/ui/transmute/transmute-in-async-fn.current.stderr index 2f5e471302b51..730e9550f9927 100644 --- a/tests/ui/transmute/transmute-in-async-fn.stderr +++ b/tests/ui/transmute/transmute-in-async-fn.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-in-async-fn.rs:4:14 + --> $DIR/transmute-in-async-fn.rs:7:14 | LL | unsafe { std::mem::transmute(1u64) } | ^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | unsafe { std::mem::transmute(1u64) } = note: target type: `u32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-in-async-fn.rs:9:31 + --> $DIR/transmute-in-async-fn.rs:12:31 | LL | let closure = || unsafe { std::mem::transmute(1u64) }; | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-in-async-fn.next.stderr b/tests/ui/transmute/transmute-in-async-fn.next.stderr new file mode 100644 index 0000000000000..730e9550f9927 --- /dev/null +++ b/tests/ui/transmute/transmute-in-async-fn.next.stderr @@ -0,0 +1,21 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-in-async-fn.rs:7:14 + | +LL | unsafe { std::mem::transmute(1u64) } + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u64` (64 bits) + = note: target type: `u32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-in-async-fn.rs:12:31 + | +LL | let closure = || unsafe { std::mem::transmute(1u64) }; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u64` (64 bits) + = note: target type: `u32` (32 bits) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-in-async-fn.rs b/tests/ui/transmute/transmute-in-async-fn.rs index 83574ee5b8bde..49270406315ec 100644 --- a/tests/ui/transmute/transmute-in-async-fn.rs +++ b/tests/ui/transmute/transmute-in-async-fn.rs @@ -1,4 +1,7 @@ -//@compile-flags: -Zmir-enable-passes=+DataflowConstProp --crate-type lib +//@ compile-flags: -Zmir-enable-passes=+DataflowConstProp --crate-type lib +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ edition:2021 pub async fn a() -> u32 { unsafe { std::mem::transmute(1u64) } diff --git a/tests/ui/transmute/transmute-same-associated-type.rs b/tests/ui/transmute/transmute-same-associated-type.rs index 8bc452c6df4f6..0ea1b799b412a 100644 --- a/tests/ui/transmute/transmute-same-associated-type.rs +++ b/tests/ui/transmute/transmute-same-associated-type.rs @@ -1,5 +1,7 @@ //! Verify transmuting is allowed when `Src` and `Dst` are the same associated type. - +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ check-pass trait Foo { diff --git a/tests/ui/transmute/transmute-slice-to-dst.rs b/tests/ui/transmute/transmute-slice-to-dst.rs index c1f77bee284dd..60386af186b6b 100644 --- a/tests/ui/transmute/transmute-slice-to-dst.rs +++ b/tests/ui/transmute/transmute-slice-to-dst.rs @@ -1,5 +1,8 @@ //! regression test for //@ build-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ compile-flags: -g pub struct Dst { diff --git a/tests/ui/transmute/transmute-type-parameters.stderr b/tests/ui/transmute/transmute-type-parameters.current.stderr similarity index 86% rename from tests/ui/transmute/transmute-type-parameters.stderr rename to tests/ui/transmute/transmute-type-parameters.current.stderr index 220b929d4fd2e..e1c3bca593af4 100644 --- a/tests/ui/transmute/transmute-type-parameters.stderr +++ b/tests/ui/transmute/transmute-type-parameters.current.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-type-parameters.rs:6:18 + --> $DIR/transmute-type-parameters.rs:8:18 | LL | let _: i32 = transmute(x); | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _: i32 = transmute(x); = note: target type: `i32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-type-parameters.rs:11:18 + --> $DIR/transmute-type-parameters.rs:13:18 | LL | let _: i32 = transmute(x); | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let _: i32 = transmute(x); = note: target type: `i32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-type-parameters.rs:16:18 + --> $DIR/transmute-type-parameters.rs:18:18 | LL | let _: i32 = transmute(x); | ^^^^^^^^^ @@ -26,7 +26,7 @@ LL | let _: i32 = transmute(x); = note: target type: `i32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-type-parameters.rs:25:18 + --> $DIR/transmute-type-parameters.rs:27:18 | LL | let _: i32 = transmute(x); | ^^^^^^^^^ @@ -35,7 +35,7 @@ LL | let _: i32 = transmute(x); = note: target type: `i32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-type-parameters.rs:35:18 + --> $DIR/transmute-type-parameters.rs:37:18 | LL | let _: i32 = transmute(x); | ^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _: i32 = transmute(x); = note: target type: `i32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-type-parameters.rs:40:18 + --> $DIR/transmute-type-parameters.rs:42:18 | LL | let _: i32 = transmute(x); | ^^^^^^^^^ diff --git a/tests/ui/transmute/transmute-type-parameters.next.stderr b/tests/ui/transmute/transmute-type-parameters.next.stderr new file mode 100644 index 0000000000000..e1c3bca593af4 --- /dev/null +++ b/tests/ui/transmute/transmute-type-parameters.next.stderr @@ -0,0 +1,57 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-type-parameters.rs:8:18 + | +LL | let _: i32 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `T` (this type does not have a fixed size) + = note: target type: `i32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-type-parameters.rs:13:18 + | +LL | let _: i32 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `(T, i32)` (size can vary because of T) + = note: target type: `i32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-type-parameters.rs:18:18 + | +LL | let _: i32 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `[T; 10]` (size can vary because of T) + = note: target type: `i32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-type-parameters.rs:27:18 + | +LL | let _: i32 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `Bad` (size can vary because of T) + = note: target type: `i32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-type-parameters.rs:37:18 + | +LL | let _: i32 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `Worse` (size can vary because of T) + = note: target type: `i32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-type-parameters.rs:42:18 + | +LL | let _: i32 = transmute(x); + | ^^^^^^^^^ + | + = note: source type: `Option` (size can vary because of T) + = note: target type: `i32` (32 bits) + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-type-parameters.rs b/tests/ui/transmute/transmute-type-parameters.rs index 5f44b2d0f0163..cb9fc3cbb86a0 100644 --- a/tests/ui/transmute/transmute-type-parameters.rs +++ b/tests/ui/transmute/transmute-type-parameters.rs @@ -1,5 +1,7 @@ // Tests that `transmute` cannot be called on type parameters. - +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver use std::mem::transmute; unsafe fn f(x: T) { diff --git a/tests/ui/transmute/transmute-zst-generics.rs b/tests/ui/transmute/transmute-zst-generics.rs index b80ec794f83f9..31ee48bac3687 100644 --- a/tests/ui/transmute/transmute-zst-generics.rs +++ b/tests/ui/transmute/transmute-zst-generics.rs @@ -1,5 +1,7 @@ //@ run-pass - +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver // Transmuting to/from ZSTs that contain generics. #![feature(transmute_generic_consts)] diff --git a/tests/ui/transmute/unnecessary-transmutation.fixed b/tests/ui/transmute/unnecessary-transmutation.current.fixed similarity index 96% rename from tests/ui/transmute/unnecessary-transmutation.fixed rename to tests/ui/transmute/unnecessary-transmutation.current.fixed index 08010ec8b84e4..567507f7dc54d 100644 --- a/tests/ui/transmute/unnecessary-transmutation.fixed +++ b/tests/ui/transmute/unnecessary-transmutation.current.fixed @@ -1,4 +1,8 @@ //@ run-rustfix +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver + #![deny(unnecessary_transmutes)] #![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] use std::mem::transmute; diff --git a/tests/ui/transmute/unnecessary-transmutation.stderr b/tests/ui/transmute/unnecessary-transmutation.current.stderr similarity index 92% rename from tests/ui/transmute/unnecessary-transmutation.stderr rename to tests/ui/transmute/unnecessary-transmutation.current.stderr index 8399f760ceb37..84532449c5dc8 100644 --- a/tests/ui/transmute/unnecessary-transmutation.stderr +++ b/tests/ui/transmute/unnecessary-transmutation.current.stderr @@ -1,12 +1,12 @@ error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:7:14 + --> $DIR/unnecessary-transmutation.rs:11:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^^^^ | = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order note: the lint level is defined here - --> $DIR/unnecessary-transmutation.rs:2:9 + --> $DIR/unnecessary-transmutation.rs:6:9 | LL | #![deny(unnecessary_transmutes)] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL + unsafe { u32::to_ne_bytes(x) } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:12:14 + --> $DIR/unnecessary-transmutation.rs:16:14 | LL | unsafe { transmute(from) } | ^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL + unsafe { (from) as u8 } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:16:29 + --> $DIR/unnecessary-transmutation.rs:20:29 | LL | pub static X: u8 = unsafe { transmute(true) }; | ^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL + pub static X: u8 = unsafe { (true) as u8 }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:18:28 + --> $DIR/unnecessary-transmutation.rs:22:28 | LL | pub const Y: u8 = unsafe { transmute(true) }; | ^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL + pub const Y: u8 = unsafe { (true) as u8 }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:24:18 + --> $DIR/unnecessary-transmutation.rs:28:18 | LL | unsafe { transmute(x) } | ^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL + unsafe { (x) as u8 } | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:30:22 + --> $DIR/unnecessary-transmutation.rs:34:22 | LL | const { unsafe { transmute::<_, u8>(true) } }; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL + const { unsafe { (true) as u8 } }; | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:33:22 + --> $DIR/unnecessary-transmutation.rs:37:22 | LL | let x: u16 = transmute(*b"01"); | ^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL + let x: u16 = u16::from_ne_bytes(*b"01"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:35:26 + --> $DIR/unnecessary-transmutation.rs:39:26 | LL | let x: [u8; 2] = transmute(x); | ^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL + let x: [u8; 2] = u16::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:37:22 + --> $DIR/unnecessary-transmutation.rs:41:22 | LL | let x: u32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -116,7 +116,7 @@ LL + let x: u32 = u32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:39:26 + --> $DIR/unnecessary-transmutation.rs:43:26 | LL | let x: [u8; 4] = transmute(x); | ^^^^^^^^^^^^ @@ -129,7 +129,7 @@ LL + let x: [u8; 4] = u32::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:41:22 + --> $DIR/unnecessary-transmutation.rs:45:22 | LL | let x: u64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -142,7 +142,7 @@ LL + let x: u64 = u64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:43:26 + --> $DIR/unnecessary-transmutation.rs:47:26 | LL | let x: [u8; 8] = transmute(x); | ^^^^^^^^^^^^ @@ -155,7 +155,7 @@ LL + let x: [u8; 8] = u64::to_ne_bytes(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:46:22 + --> $DIR/unnecessary-transmutation.rs:50:22 | LL | let y: i16 = transmute(*b"01"); | ^^^^^^^^^^^^^^^^^ @@ -168,7 +168,7 @@ LL + let y: i16 = i16::from_ne_bytes(*b"01"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:48:26 + --> $DIR/unnecessary-transmutation.rs:52:26 | LL | let y: [u8; 2] = transmute(y); | ^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let y: [u8; 2] = i16::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:50:22 + --> $DIR/unnecessary-transmutation.rs:54:22 | LL | let y: i32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -194,7 +194,7 @@ LL + let y: i32 = i32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:52:26 + --> $DIR/unnecessary-transmutation.rs:56:26 | LL | let y: [u8; 4] = transmute(y); | ^^^^^^^^^^^^ @@ -207,7 +207,7 @@ LL + let y: [u8; 4] = i32::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:54:22 + --> $DIR/unnecessary-transmutation.rs:58:22 | LL | let y: i64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -220,7 +220,7 @@ LL + let y: i64 = i64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:56:26 + --> $DIR/unnecessary-transmutation.rs:60:26 | LL | let y: [u8; 8] = transmute(y); | ^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL + let y: [u8; 8] = i64::to_ne_bytes(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:59:22 + --> $DIR/unnecessary-transmutation.rs:63:22 | LL | let z: f32 = transmute(*b"0123"); | ^^^^^^^^^^^^^^^^^^^ @@ -246,7 +246,7 @@ LL + let z: f32 = f32::from_ne_bytes(*b"0123"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:61:26 + --> $DIR/unnecessary-transmutation.rs:65:26 | LL | let z: [u8; 4] = transmute(z); | ^^^^^^^^^^^^ @@ -259,7 +259,7 @@ LL + let z: [u8; 4] = f32::to_ne_bytes(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:63:22 + --> $DIR/unnecessary-transmutation.rs:67:22 | LL | let z: f64 = transmute(*b"feriscat"); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -272,7 +272,7 @@ LL + let z: f64 = f64::from_ne_bytes(*b"feriscat"); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:65:26 + --> $DIR/unnecessary-transmutation.rs:69:26 | LL | let z: [u8; 8] = transmute(z); | ^^^^^^^^^^^^ @@ -285,7 +285,7 @@ LL + let z: [u8; 8] = f64::to_ne_bytes(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:68:22 + --> $DIR/unnecessary-transmutation.rs:72:22 | LL | let y: u32 = transmute('πŸ¦€'); | ^^^^^^^^^^^^^^^ @@ -297,7 +297,7 @@ LL + let y: u32 = u32::from('πŸ¦€'); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:70:23 + --> $DIR/unnecessary-transmutation.rs:74:23 | LL | let y: char = transmute(y); | ^^^^^^^^^^^^ @@ -310,7 +310,7 @@ LL + let y: char = char::from_u32_unchecked(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:72:22 + --> $DIR/unnecessary-transmutation.rs:76:22 | LL | let y: i32 = transmute('🐱'); | ^^^^^^^^^^^^^^^ @@ -322,7 +322,7 @@ LL + let y: i32 = u32::from('🐱').cast_signed(); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:74:23 + --> $DIR/unnecessary-transmutation.rs:78:23 | LL | let y: char = transmute(y); | ^^^^^^^^^^^^ @@ -335,7 +335,7 @@ LL + let y: char = char::from_u32_unchecked(i32::cast_unsigned(y)); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:77:22 + --> $DIR/unnecessary-transmutation.rs:81:22 | LL | let x: u16 = transmute(8i16); | ^^^^^^^^^^^^^^^ @@ -347,7 +347,7 @@ LL + let x: u16 = i16::cast_unsigned(8i16); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:79:22 + --> $DIR/unnecessary-transmutation.rs:83:22 | LL | let x: i16 = transmute(x); | ^^^^^^^^^^^^ @@ -359,7 +359,7 @@ LL + let x: i16 = u16::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:81:22 + --> $DIR/unnecessary-transmutation.rs:85:22 | LL | let x: u32 = transmute(4i32); | ^^^^^^^^^^^^^^^ @@ -371,7 +371,7 @@ LL + let x: u32 = i32::cast_unsigned(4i32); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:83:22 + --> $DIR/unnecessary-transmutation.rs:87:22 | LL | let x: i32 = transmute(x); | ^^^^^^^^^^^^ @@ -383,7 +383,7 @@ LL + let x: i32 = u32::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:85:22 + --> $DIR/unnecessary-transmutation.rs:89:22 | LL | let x: u64 = transmute(7i64); | ^^^^^^^^^^^^^^^ @@ -395,7 +395,7 @@ LL + let x: u64 = i64::cast_unsigned(7i64); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:87:22 + --> $DIR/unnecessary-transmutation.rs:91:22 | LL | let x: i64 = transmute(x); | ^^^^^^^^^^^^ @@ -407,7 +407,7 @@ LL + let x: i64 = u64::cast_signed(x); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:90:22 + --> $DIR/unnecessary-transmutation.rs:94:22 | LL | let y: f32 = transmute(1u32); | ^^^^^^^^^^^^^^^ @@ -419,7 +419,7 @@ LL + let y: f32 = f32::from_bits(1u32); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:92:22 + --> $DIR/unnecessary-transmutation.rs:96:22 | LL | let y: u32 = transmute(y); | ^^^^^^^^^^^^ @@ -431,7 +431,7 @@ LL + let y: u32 = f32::to_bits(y); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:94:22 + --> $DIR/unnecessary-transmutation.rs:98:22 | LL | let y: f64 = transmute(3u64); | ^^^^^^^^^^^^^^^ @@ -443,7 +443,7 @@ LL + let y: f64 = f64::from_bits(3u64); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:96:22 + --> $DIR/unnecessary-transmutation.rs:100:22 | LL | let y: u64 = transmute(2.0); | ^^^^^^^^^^^^^^ @@ -455,7 +455,7 @@ LL + let y: u64 = f64::to_bits(2.0); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:99:22 + --> $DIR/unnecessary-transmutation.rs:103:22 | LL | let y: f64 = transmute(1i64); | ^^^^^^^^^^^^^^^ @@ -467,7 +467,7 @@ LL + let y: f64 = f64::from_bits(i64::cast_unsigned(1i64)); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:101:22 + --> $DIR/unnecessary-transmutation.rs:105:22 | LL | let y: i64 = transmute(1f64); | ^^^^^^^^^^^^^^^ @@ -479,7 +479,7 @@ LL + let y: i64 = f64::to_bits(1f64).cast_signed(); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:106:21 + --> $DIR/unnecessary-transmutation.rs:110:21 | LL | let z: u8 = transmute(z); | ^^^^^^^^^^^^ @@ -491,7 +491,7 @@ LL + let z: u8 = u8::from(z); | error: unnecessary transmute - --> $DIR/unnecessary-transmutation.rs:111:21 + --> $DIR/unnecessary-transmutation.rs:115:21 | LL | let z: i8 = transmute(z); | ^^^^^^^^^^^^ diff --git a/tests/ui/transmute/unnecessary-transmutation.next.fixed b/tests/ui/transmute/unnecessary-transmutation.next.fixed new file mode 100644 index 0000000000000..567507f7dc54d --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmutation.next.fixed @@ -0,0 +1,118 @@ +//@ run-rustfix +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver + +#![deny(unnecessary_transmutes)] +#![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] +use std::mem::transmute; + +pub fn bytes_at_home(x: u32) -> [u8; 4] { + unsafe { u32::to_ne_bytes(x) } + //~^ ERROR +} + +pub const fn intinator_const(from: bool) -> u8 { + unsafe { (from) as u8 } + //~^ ERROR +} + +pub static X: u8 = unsafe { (true) as u8 }; +//~^ ERROR +pub const Y: u8 = unsafe { (true) as u8 }; +//~^ ERROR + +pub struct Z {} +impl Z { + pub const fn intinator_assoc(x: bool) -> u8 { + unsafe { (x) as u8 } + //~^ ERROR + } +} + +fn main() { + const { unsafe { (true) as u8 } }; + //~^ ERROR + unsafe { + let x: u16 = u16::from_ne_bytes(*b"01"); + //~^ ERROR + let x: [u8; 2] = u16::to_ne_bytes(x); + //~^ ERROR + let x: u32 = u32::from_ne_bytes(*b"0123"); + //~^ ERROR + let x: [u8; 4] = u32::to_ne_bytes(x); + //~^ ERROR + let x: u64 = u64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let x: [u8; 8] = u64::to_ne_bytes(x); + //~^ ERROR + + let y: i16 = i16::from_ne_bytes(*b"01"); + //~^ ERROR + let y: [u8; 2] = i16::to_ne_bytes(y); + //~^ ERROR + let y: i32 = i32::from_ne_bytes(*b"0123"); + //~^ ERROR + let y: [u8; 4] = i32::to_ne_bytes(y); + //~^ ERROR + let y: i64 = i64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let y: [u8; 8] = i64::to_ne_bytes(y); + //~^ ERROR + + let z: f32 = f32::from_ne_bytes(*b"0123"); + //~^ ERROR + let z: [u8; 4] = f32::to_ne_bytes(z); + //~^ ERROR + let z: f64 = f64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let z: [u8; 8] = f64::to_ne_bytes(z); + //~^ ERROR + + let y: u32 = u32::from('πŸ¦€'); + //~^ ERROR + let y: char = char::from_u32_unchecked(y); + //~^ ERROR + let y: i32 = u32::from('🐱').cast_signed(); + //~^ ERROR + let y: char = char::from_u32_unchecked(i32::cast_unsigned(y)); + //~^ ERROR + + let x: u16 = i16::cast_unsigned(8i16); + //~^ ERROR + let x: i16 = u16::cast_signed(x); + //~^ ERROR + let x: u32 = i32::cast_unsigned(4i32); + //~^ ERROR + let x: i32 = u32::cast_signed(x); + //~^ ERROR + let x: u64 = i64::cast_unsigned(7i64); + //~^ ERROR + let x: i64 = u64::cast_signed(x); + //~^ ERROR + + let y: f32 = f32::from_bits(1u32); + //~^ ERROR + let y: u32 = f32::to_bits(y); + //~^ ERROR + let y: f64 = f64::from_bits(3u64); + //~^ ERROR + let y: u64 = f64::to_bits(2.0); + //~^ ERROR + + let y: f64 = f64::from_bits(i64::cast_unsigned(1i64)); + //~^ ERROR + let y: i64 = f64::to_bits(1f64).cast_signed(); + //~^ ERROR + + let z: bool = transmute(1u8); + // clippy + let z: u8 = u8::from(z); + //~^ ERROR + + let z: bool = transmute(1i8); + // clippy + let z: i8 = i8::from(z); + //~^ ERROR + } +} diff --git a/tests/ui/transmute/unnecessary-transmutation.next.stderr b/tests/ui/transmute/unnecessary-transmutation.next.stderr new file mode 100644 index 0000000000000..84532449c5dc8 --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmutation.next.stderr @@ -0,0 +1,506 @@ +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:11:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +note: the lint level is defined here + --> $DIR/unnecessary-transmutation.rs:6:9 + | +LL | #![deny(unnecessary_transmutes)] + | ^^^^^^^^^^^^^^^^^^^^^^ +help: replace this with + | +LL - unsafe { transmute(x) } +LL + unsafe { u32::to_ne_bytes(x) } + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:16:14 + | +LL | unsafe { transmute(from) } + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - unsafe { transmute(from) } +LL + unsafe { (from) as u8 } + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:20:29 + | +LL | pub static X: u8 = unsafe { transmute(true) }; + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - pub static X: u8 = unsafe { transmute(true) }; +LL + pub static X: u8 = unsafe { (true) as u8 }; + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:22:28 + | +LL | pub const Y: u8 = unsafe { transmute(true) }; + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - pub const Y: u8 = unsafe { transmute(true) }; +LL + pub const Y: u8 = unsafe { (true) as u8 }; + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:28:18 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - unsafe { transmute(x) } +LL + unsafe { (x) as u8 } + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:34:22 + | +LL | const { unsafe { transmute::<_, u8>(true) } }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - const { unsafe { transmute::<_, u8>(true) } }; +LL + const { unsafe { (true) as u8 } }; + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:37:22 + | +LL | let x: u16 = transmute(*b"01"); + | ^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let x: u16 = transmute(*b"01"); +LL + let x: u16 = u16::from_ne_bytes(*b"01"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:39:26 + | +LL | let x: [u8; 2] = transmute(x); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let x: [u8; 2] = transmute(x); +LL + let x: [u8; 2] = u16::to_ne_bytes(x); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:41:22 + | +LL | let x: u32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let x: u32 = transmute(*b"0123"); +LL + let x: u32 = u32::from_ne_bytes(*b"0123"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:43:26 + | +LL | let x: [u8; 4] = transmute(x); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let x: [u8; 4] = transmute(x); +LL + let x: [u8; 4] = u32::to_ne_bytes(x); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:45:22 + | +LL | let x: u64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let x: u64 = transmute(*b"feriscat"); +LL + let x: u64 = u64::from_ne_bytes(*b"feriscat"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:47:26 + | +LL | let x: [u8; 8] = transmute(x); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let x: [u8; 8] = transmute(x); +LL + let x: [u8; 8] = u64::to_ne_bytes(x); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:50:22 + | +LL | let y: i16 = transmute(*b"01"); + | ^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let y: i16 = transmute(*b"01"); +LL + let y: i16 = i16::from_ne_bytes(*b"01"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:52:26 + | +LL | let y: [u8; 2] = transmute(y); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let y: [u8; 2] = transmute(y); +LL + let y: [u8; 2] = i16::to_ne_bytes(y); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:54:22 + | +LL | let y: i32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let y: i32 = transmute(*b"0123"); +LL + let y: i32 = i32::from_ne_bytes(*b"0123"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:56:26 + | +LL | let y: [u8; 4] = transmute(y); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let y: [u8; 4] = transmute(y); +LL + let y: [u8; 4] = i32::to_ne_bytes(y); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:58:22 + | +LL | let y: i64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let y: i64 = transmute(*b"feriscat"); +LL + let y: i64 = i64::from_ne_bytes(*b"feriscat"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:60:26 + | +LL | let y: [u8; 8] = transmute(y); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let y: [u8; 8] = transmute(y); +LL + let y: [u8; 8] = i64::to_ne_bytes(y); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:63:22 + | +LL | let z: f32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let z: f32 = transmute(*b"0123"); +LL + let z: f32 = f32::from_ne_bytes(*b"0123"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:65:26 + | +LL | let z: [u8; 4] = transmute(z); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let z: [u8; 4] = transmute(z); +LL + let z: [u8; 4] = f32::to_ne_bytes(z); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:67:22 + | +LL | let z: f64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let z: f64 = transmute(*b"feriscat"); +LL + let z: f64 = f64::from_ne_bytes(*b"feriscat"); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:69:26 + | +LL | let z: [u8; 8] = transmute(z); + | ^^^^^^^^^^^^ + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +help: replace this with + | +LL - let z: [u8; 8] = transmute(z); +LL + let z: [u8; 8] = f64::to_ne_bytes(z); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:72:22 + | +LL | let y: u32 = transmute('πŸ¦€'); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: u32 = transmute('πŸ¦€'); +LL + let y: u32 = u32::from('πŸ¦€'); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:74:23 + | +LL | let y: char = transmute(y); + | ^^^^^^^^^^^^ + | + = help: consider using `char::from_u32(…).unwrap()` +help: replace this with + | +LL - let y: char = transmute(y); +LL + let y: char = char::from_u32_unchecked(y); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:76:22 + | +LL | let y: i32 = transmute('🐱'); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: i32 = transmute('🐱'); +LL + let y: i32 = u32::from('🐱').cast_signed(); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:78:23 + | +LL | let y: char = transmute(y); + | ^^^^^^^^^^^^ + | + = help: consider using `char::from_u32(i32::cast_unsigned(…)).unwrap()` +help: replace this with + | +LL - let y: char = transmute(y); +LL + let y: char = char::from_u32_unchecked(i32::cast_unsigned(y)); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:81:22 + | +LL | let x: u16 = transmute(8i16); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let x: u16 = transmute(8i16); +LL + let x: u16 = i16::cast_unsigned(8i16); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:83:22 + | +LL | let x: i16 = transmute(x); + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - let x: i16 = transmute(x); +LL + let x: i16 = u16::cast_signed(x); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:85:22 + | +LL | let x: u32 = transmute(4i32); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let x: u32 = transmute(4i32); +LL + let x: u32 = i32::cast_unsigned(4i32); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:87:22 + | +LL | let x: i32 = transmute(x); + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - let x: i32 = transmute(x); +LL + let x: i32 = u32::cast_signed(x); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:89:22 + | +LL | let x: u64 = transmute(7i64); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let x: u64 = transmute(7i64); +LL + let x: u64 = i64::cast_unsigned(7i64); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:91:22 + | +LL | let x: i64 = transmute(x); + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - let x: i64 = transmute(x); +LL + let x: i64 = u64::cast_signed(x); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:94:22 + | +LL | let y: f32 = transmute(1u32); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: f32 = transmute(1u32); +LL + let y: f32 = f32::from_bits(1u32); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:96:22 + | +LL | let y: u32 = transmute(y); + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: u32 = transmute(y); +LL + let y: u32 = f32::to_bits(y); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:98:22 + | +LL | let y: f64 = transmute(3u64); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: f64 = transmute(3u64); +LL + let y: f64 = f64::from_bits(3u64); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:100:22 + | +LL | let y: u64 = transmute(2.0); + | ^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: u64 = transmute(2.0); +LL + let y: u64 = f64::to_bits(2.0); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:103:22 + | +LL | let y: f64 = transmute(1i64); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: f64 = transmute(1i64); +LL + let y: f64 = f64::from_bits(i64::cast_unsigned(1i64)); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:105:22 + | +LL | let y: i64 = transmute(1f64); + | ^^^^^^^^^^^^^^^ + | +help: replace this with + | +LL - let y: i64 = transmute(1f64); +LL + let y: i64 = f64::to_bits(1f64).cast_signed(); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:110:21 + | +LL | let z: u8 = transmute(z); + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - let z: u8 = transmute(z); +LL + let z: u8 = u8::from(z); + | + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:115:21 + | +LL | let z: i8 = transmute(z); + | ^^^^^^^^^^^^ + | +help: replace this with + | +LL - let z: i8 = transmute(z); +LL + let z: i8 = i8::from(z); + | + +error: aborting due to 40 previous errors + diff --git a/tests/ui/transmute/unnecessary-transmutation.rs b/tests/ui/transmute/unnecessary-transmutation.rs index 43eefb97dc28f..273529f5e5d78 100644 --- a/tests/ui/transmute/unnecessary-transmutation.rs +++ b/tests/ui/transmute/unnecessary-transmutation.rs @@ -1,4 +1,8 @@ //@ run-rustfix +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver + #![deny(unnecessary_transmutes)] #![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] use std::mem::transmute; diff --git a/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs b/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs index 756ce7b3d5074..5f7ce0005bd0e 100644 --- a/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs +++ b/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs @@ -1,4 +1,7 @@ //@ aux-crate: zerocopy=unnecessary-transmute-path-remap-ice-140277-trans.rs +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ [next] compile-flags: -Znext-solver //@ check-pass // tests for a regression in linting for unnecessary transmutes // where a span was inacessible for snippet procuring,