diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index ecf8f993f90b..93388ddd2407 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -7,6 +7,6 @@ tracking issue or there are none, feel free to ignore this. This PR will get automatically assigned to a reviewer. In case you would like a specific user to review your work, you can assign it to them by using - r\? (with the `\` removed) + r? --> diff --git a/Cargo.lock b/Cargo.lock index 62541eb9d479..26971e8a120d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,9 +185,6 @@ name = "anyhow" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" -dependencies = [ - "backtrace", -] [[package]] name = "ar_archive_writer" @@ -3499,6 +3496,7 @@ name = "rustc_codegen_llvm" version = "0.0.0" dependencies = [ "bitflags", + "gimli 0.30.0", "itertools", "libc", "measureme", @@ -4117,7 +4115,6 @@ name = "rustc_middle" version = "0.0.0" dependencies = [ "bitflags", - "derive-where", "either", "field-offset", "gsgdt", diff --git a/RELEASES.md b/RELEASES.md index 99733bade32c..fe557a08a9dc 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,119 @@ +Version 1.84.0 (2025-01-09) +========================== + + + +Language +-------- +- [Allow `#[deny]` inside `#[forbid]` as a no-op](https://github.com/rust-lang/rust/pull/121560/) +- [Show a warning when `-Ctarget-feature` is used to toggle features that can lead to unsoundness due to ABI mismatches](https://github.com/rust-lang/rust/pull/129884) +- [Use the next-generation trait solver in coherence](https://github.com/rust-lang/rust/pull/130654) +- [Allow coercions to drop the principal of trait objects](https://github.com/rust-lang/rust/pull/131857) +- [Support `/` as the path separator for `include!()` in all cases on Windows](https://github.com/rust-lang/rust/pull/125205) +- [Taking a raw ref (`raw (const|mut)`) of a deref of a pointer (`*ptr`) is now safe](https://github.com/rust-lang/rust/pull/129248) +- [Stabilize s390x inline assembly](https://github.com/rust-lang/rust/pull/131258) +- [Stabilize Arm64EC inline assembly](https://github.com/rust-lang/rust/pull/131781) +- [Lint against creating pointers to immediately dropped temporaries](https://github.com/rust-lang/rust/pull/128985) +- [Execute drop glue when unwinding in an `extern "C"` function](https://github.com/rust-lang/rust/pull/129582) + + + +Compiler +-------- +- [Add `--print host-tuple` flag to print the host target tuple and affirm the "target tuple" terminology over "target triple"](https://github.com/rust-lang/rust/pull/125579) +- [Declaring functions with a calling convention not supported on the current target now triggers a hard error](https://github.com/rust-lang/rust/pull/129935) +- [Set up indirect access to external data for `loongarch64-unknown-linux-{musl,ohos}`](https://github.com/rust-lang/rust/pull/131583) +- [Enable XRay instrumentation for LoongArch Linux targets](https://github.com/rust-lang/rust/pull/131818) +- [Extend the `unexpected_cfgs` lint to also warn in external macros](https://github.com/rust-lang/rust/pull/132577) +- [Stabilize WebAssembly `multivalue`, `reference-types`, and `tail-call` target features](https://github.com/rust-lang/rust/pull/131080) +- [Added Tier 2 support for the `wasm32v1-none` target](https://github.com/rust-lang/rust/pull/131487) + + + +Libraries +--------- +- [Implement `From<&mut {slice}>` for `Box/Rc/Arc<{slice}>`](https://github.com/rust-lang/rust/pull/129329) +- [Move `::copysign`, `::abs`, `::signum` to `core`](https://github.com/rust-lang/rust/pull/131304) +- [Add `LowerExp` and `UpperExp` implementations to `NonZero`](https://github.com/rust-lang/rust/pull/131377) +- [Implement `FromStr` for `CString` and `TryFrom` for `String`](https://github.com/rust-lang/rust/pull/130608) +- [`std::os::darwin` has been made public](https://github.com/rust-lang/rust/pull/130635) + + + +Stabilized APIs +--------------- + +- [`Ipv6Addr::is_unique_local`](https://doc.rust-lang.org/stable/core/net/struct.Ipv6Addr.html#method.is_unique_local) +- [`Ipv6Addr::is_unicast_link_local`](https://doc.rust-lang.org/stable/core/net/struct.Ipv6Addr.html#method.is_unicast_link_local) +- [`core::ptr::with_exposed_provenance`](https://doc.rust-lang.org/stable/core/ptr/fn.with_exposed_provenance.html) +- [`core::ptr::with_exposed_provenance_mut`](https://doc.rust-lang.org/stable/core/ptr/fn.with_exposed_provenance_mut.html) +- [`::addr`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.addr) +- [`::expose_provenance`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.expose_provenance) +- [`::with_addr`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.with_addr) +- [`::map_addr`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.map_addr) +- [`::isqrt`](https://doc.rust-lang.org/stable/core/primitive.i32.html#method.isqrt) +- [`::checked_isqrt`](https://doc.rust-lang.org/stable/core/primitive.i32.html#method.checked_isqrt) +- [`::isqrt`](https://doc.rust-lang.org/stable/core/primitive.u32.html#method.isqrt) +- [`NonZero::isqrt`](https://doc.rust-lang.org/stable/core/num/struct.NonZero.html#impl-NonZero%3Cu128%3E/method.isqrt) +- [`core::ptr::without_provenance`](https://doc.rust-lang.org/stable/core/ptr/fn.without_provenance.html) +- [`core::ptr::without_provenance_mut`](https://doc.rust-lang.org/stable/core/ptr/fn.without_provenance_mut.html) +- [`core::ptr::dangling`](https://doc.rust-lang.org/stable/core/ptr/fn.dangling.html) +- [`core::ptr::dangling_mut`](https://doc.rust-lang.org/stable/core/ptr/fn.dangling_mut.html) + +These APIs are now stable in const contexts + +- [`AtomicBool::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicBool.html#method.from_ptr) +- [`AtomicPtr::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicPtr.html#method.from_ptr) +- [`AtomicU8::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU8.html#method.from_ptr) +- [`AtomicU16::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU16.html#method.from_ptr) +- [`AtomicU32::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU32.html#method.from_ptr) +- [`AtomicU64::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU64.html#method.from_ptr) +- [`AtomicUsize::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicUsize.html#method.from_ptr) +- [`AtomicI8::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicI8.html#method.from_ptr) +- [`AtomicI16::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicI16.html#method.from_ptr) +- [`AtomicI32::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicI32.html#method.from_ptr) +- [`AtomicI64::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicI64.html#method.from_ptr) +- [`AtomicIsize::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicIsize.html#method.from_ptr) +- [`::is_null`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.is_null-1) +- [`::as_ref`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.as_ref-1) +- [`::as_mut`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.as_mut) +- [`Pin::new`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.new) +- [`Pin::new_unchecked`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.new_unchecked) +- [`Pin::get_ref`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.get_ref) +- [`Pin::into_ref`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.into_ref) +- [`Pin::get_mut`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.get_mut) +- [`Pin::get_unchecked_mut`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.get_unchecked_mut) +- [`Pin::static_ref`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.static_ref) +- [`Pin::static_mut`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.static_mut) + + + +Cargo +----- +- [Stabilize MSRV-aware resolver config](https://github.com/rust-lang/cargo/pull/14639/) +- [Stabilize resolver v3](https://github.com/rust-lang/cargo/pull/14754/) + + + +Rustdoc +------- + +- [rustdoc-search: improve type-driven search](https://github.com/rust-lang/rust/pull/127589) + + + +Compatibility Notes +------------------- +- [Enable by default the `LSX` target feature for LoongArch Linux targets](https://github.com/rust-lang/rust/pull/132140) +- [The unstable `-Zprofile` flag (“gcov-style” coverage instrumentation) has been removed.](https://github.com/rust-lang/rust/pull/131829) This does not affect the stable flags for coverage instrumentation (`-Cinstrument-coverage`) and profile-guided optimization (`-Cprofile-generate`, `-Cprofile-use`), which are unrelated and remain available. +- Support for the target named `wasm32-wasi` has been removed as the target is now named `wasm32-wasip1`. This completes the [transition](https://github.com/rust-lang/compiler-team/issues/607) [plan](https://github.com/rust-lang/compiler-team/issues/695) for this target following [the introduction of `wasm32-wasip1`](https://github.com/rust-lang/rust/pull/120468) in Rust 1.78. Compiler warnings on [use of `wasm32-wasi`](https://github.com/rust-lang/rust/pull/126662) introduced in Rust 1.81 are now gone as well as the target is removed. +- [The syntax `&pin (mut|const) T` is now parsed as a type which in theory could affect macro expansion results in some edge cases](https://github.com/rust-lang/rust/pull/130635#issuecomment-2375462821) +- [Legacy syntax for calling `std::arch` functions is no longer permitted to declare items or bodies (such as closures, inline consts, or async blocks).](https://github.com/rust-lang/rust/pull/130443#issuecomment-2445678945) +- The `wasm32-unknown-emscripten` target's binary release of the standard library is now [built with the latest emsdk 3.1.68](https://github.com/rust-lang/rust/pull/131533), which fixes an ABI-incompatibility with Emscripten >= 3.1.42. If you are locally using a version of emsdk with an incompatible ABI (e.g. before 3.1.42 or a future one), you should build your code with `-Zbuild-std` to ensure that `std` uses the correct ABI. +- [Declaring functions with a calling convention not supported on the current target now triggers a hard error](https://github.com/rust-lang/rust/pull/129935) +- [The next-generation trait solver is now enabled for coherence, fixing multiple soundness issues](https://github.com/rust-lang/rust/pull/130654) + Version 1.83.0 (2024-11-28) ========================== diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index a4ab2561b721..40e7d6430fde 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -120,8 +120,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_range_end(end, e2.is_some()), ); } - // FIXME(guard_patterns): lower pattern guards to HIR - PatKind::Guard(inner, _) => pattern = inner, + PatKind::Guard(inner, cond) => { + break hir::PatKind::Guard(self.lower_pat(inner), self.lower_expr(cond)); + } PatKind::Slice(pats) => break self.lower_pat_slice(pats), PatKind::Rest => { // If we reach here the `..` pattern is not semantically allowed. diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 3fbf12101861..c8500098add1 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -242,7 +242,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } - ast::ItemKind::Struct(..) => { + ast::ItemKind::Struct(..) | ast::ItemKind::Enum(..) | ast::ItemKind::Union(..) => { for attr in attr::filter_by_name(&i.attrs, sym::repr) { for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { if item.has_name(sym::simd) { diff --git a/compiler/rustc_attr_data_structures/src/stability.rs b/compiler/rustc_attr_data_structures/src/stability.rs index 021fe40e3e04..3c77d4c766c5 100644 --- a/compiler/rustc_attr_data_structures/src/stability.rs +++ b/compiler/rustc_attr_data_structures/src/stability.rs @@ -9,7 +9,12 @@ use crate::RustcVersion; /// `since` field of the `#[stable]` attribute. /// /// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591). -pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION"; +pub const VERSION_PLACEHOLDER: &str = concat!("CURRENT_RUSTC_VERSIO", "N"); +// Note that the `concat!` macro above prevents `src/tools/replace-version-placeholder` from +// replacing the constant with the current version. Hardcoding the tool to skip this file doesn't +// work as the file can (and at some point will) be moved around. +// +// Turning the `concat!` macro into a string literal will make Pietro cry. That'd be sad :( /// Represents the following attributes: /// diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index ee4b2f95cb15..ada20e5c614f 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -213,6 +213,10 @@ borrowck_suggest_create_fresh_reborrow = borrowck_suggest_iterate_over_slice = consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop +borrowck_tail_expr_drop_order = relative drop order changing in Rust 2024 + .label = this temporary value will be dropped at the end of the block + .note = consider using a `let` binding to ensure the value will live long enough + borrowck_ty_no_impl_copy = {$is_partial_move -> [true] partial move diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index ff838fbbb886..a29833464fb8 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -11,7 +11,6 @@ use rustc_mir_dataflow::move_paths::MoveData; use tracing::debug; use crate::BorrowIndex; -use crate::path_utils::allow_two_phase_borrow; use crate::place_ext::PlaceExt; pub struct BorrowSet<'tcx> { @@ -350,7 +349,7 @@ impl<'a, 'tcx> GatherBorrows<'a, 'tcx> { start_location, assigned_place, borrow_index, ); - if !allow_two_phase_borrow(kind) { + if !kind.allows_two_phase_borrow() { debug!(" -> {:?}", start_location); return; } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 8e5944d6cf45..2d993a3fd16f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1516,15 +1516,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { }); self.explain_why_borrow_contains_point(location, borrow, None) - .add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - Some(borrow_span), - None, - ); + .add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None); self.suggest_copy_for_type_in_cloned_ref(&mut err, place); let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); if let Some(expr) = self.find_expr(borrow_span) { @@ -1591,15 +1583,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { }); self.explain_why_borrow_contains_point(location, borrow, None) - .add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - None, - None, - ); + .add_explanation_to_diagnostic(&self, &mut err, "", None, None); err } @@ -1886,9 +1870,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } explanation.add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, + &self, &mut err, first_borrow_desc, None, @@ -3046,15 +3028,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let BorrowExplanation::MustBeValidFor { .. } = explanation { } else { - explanation.add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - None, - None, - ); + explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None); } } else { err.span_label(borrow_span, "borrowed value does not live long enough"); @@ -3067,15 +3041,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } }); - explanation.add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - Some(borrow_span), - None, - ); + explanation.add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None); } err @@ -3128,15 +3094,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { _ => {} } - explanation.add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - None, - None, - ); + explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None); self.buffer_error(err); } @@ -3309,15 +3267,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } _ => {} } - explanation.add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - None, - None, - ); + explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None); borrow_spans.args_subdiag(&mut err, |args_span| { crate::session_diagnostics::CaptureArgLabel::Capture { @@ -3808,15 +3758,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } }); - self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - None, - None, - ); + self.explain_why_borrow_contains_point(location, loan, None) + .add_explanation_to_diagnostic(&self, &mut err, "", None, None); self.explain_deref_coercion(loan, &mut err); diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 22f7f708419b..a52dc447d768 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -5,10 +5,9 @@ use std::assert_matches::assert_matches; -use rustc_errors::{Applicability, Diag}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; -use rustc_index::IndexSlice; use rustc_infer::infer::NllRegionVariableOrigin; use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::mir::{ @@ -17,14 +16,16 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; -use rustc_span::{DesugaringKind, Span, Symbol, kw, sym}; +use rustc_middle::util::CallKind; +use rustc_span::{DesugaringKind, Span, kw, sym}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use tracing::{debug, instrument}; use super::{RegionName, UseSpans, find_use}; use crate::borrow_set::BorrowData; +use crate::constraints::OutlivesConstraint; use crate::nll::ConstraintDescription; -use crate::region_infer::{BlameConstraint, Cause, ExtraConstraintInfo}; +use crate::region_infer::{BlameConstraint, Cause}; use crate::{MirBorrowckCtxt, WriteKind}; #[derive(Debug)] @@ -42,7 +43,7 @@ pub(crate) enum BorrowExplanation<'tcx> { span: Span, region_name: RegionName, opt_place_desc: Option, - extra_info: Vec, + path: Vec>, }, Unexplained, } @@ -60,16 +61,18 @@ impl<'tcx> BorrowExplanation<'tcx> { pub(crate) fn is_explained(&self) -> bool { !matches!(self, BorrowExplanation::Unexplained) } - pub(crate) fn add_explanation_to_diagnostic( + pub(crate) fn add_explanation_to_diagnostic( &self, - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - local_names: &IndexSlice>, - err: &mut Diag<'_>, + cx: &MirBorrowckCtxt<'_, '_, 'tcx>, + err: &mut Diag<'_, G>, borrow_desc: &str, borrow_span: Option, multiple_borrow_span: Option<(Span, Span)>, ) { + let tcx = cx.infcx.tcx; + let body = cx.body; + let local_names = &cx.local_names; + if let Some(span) = borrow_span { let def_id = body.source.def_id(); if let Some(node) = tcx.hir().get_if_local(def_id) @@ -305,7 +308,7 @@ impl<'tcx> BorrowExplanation<'tcx> { ref region_name, ref opt_place_desc, from_closure: _, - ref extra_info, + ref path, } => { region_name.highlight_region_name(err); @@ -327,13 +330,8 @@ impl<'tcx> BorrowExplanation<'tcx> { ); }; - for extra in extra_info { - match extra { - ExtraConstraintInfo::PlaceholderFromPredicate(span) => { - err.span_note(*span, "due to current limitations in the borrow checker, this implies a `'static` lifetime"); - } - } - } + cx.add_placeholder_from_predicate_note(err, &path); + cx.add_sized_or_copy_bound_info(err, category, &path); if let ConstraintCategory::Cast { is_implicit_coercion: true, @@ -348,10 +346,10 @@ impl<'tcx> BorrowExplanation<'tcx> { } } - fn add_object_lifetime_default_note( + fn add_object_lifetime_default_note( &self, tcx: TyCtxt<'tcx>, - err: &mut Diag<'_>, + err: &mut Diag<'_, G>, unsize_ty: Ty<'tcx>, ) { if let ty::Adt(def, args) = unsize_ty.kind() { @@ -405,9 +403,9 @@ impl<'tcx> BorrowExplanation<'tcx> { } } - fn add_lifetime_bound_suggestion_to_diagnostic( + fn add_lifetime_bound_suggestion_to_diagnostic( &self, - err: &mut Diag<'_>, + err: &mut Diag<'_, G>, category: &ConstraintCategory<'tcx>, span: Span, region_name: &RegionName, @@ -434,14 +432,14 @@ impl<'tcx> BorrowExplanation<'tcx> { } } -fn suggest_rewrite_if_let( +fn suggest_rewrite_if_let( tcx: TyCtxt<'_>, expr: &hir::Expr<'_>, pat: &str, init: &hir::Expr<'_>, conseq: &hir::Expr<'_>, alt: Option<&hir::Expr<'_>>, - err: &mut Diag<'_>, + err: &mut Diag<'_, G>, ) { let source_map = tcx.sess.source_map(); err.span_note( @@ -486,8 +484,9 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { &self, borrow_region: RegionVid, outlived_region: RegionVid, - ) -> (ConstraintCategory<'tcx>, bool, Span, Option, Vec) { - let (blame_constraint, extra_info) = self.regioncx.best_blame_constraint( + ) -> (ConstraintCategory<'tcx>, bool, Span, Option, Vec>) + { + let (blame_constraint, path) = self.regioncx.best_blame_constraint( borrow_region, NllRegionVariableOrigin::FreeRegion, |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region), @@ -496,7 +495,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { let outlived_fr_name = self.give_region_a_name(outlived_region); - (category, from_closure, cause.span, outlived_fr_name, extra_info) + (category, from_closure, cause.span, outlived_fr_name, path) } /// Returns structured explanation for *why* the borrow contains the @@ -595,7 +594,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { None => { if let Some(region) = self.to_error_region_vid(borrow_region_vid) { - let (category, from_closure, span, region_name, extra_info) = + let (category, from_closure, span, region_name, path) = self.free_region_constraint_info(borrow_region_vid, region); if let Some(region_name) = region_name { let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref()); @@ -605,7 +604,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { span, region_name, opt_place_desc, - extra_info, + path, } } else { debug!("Could not generate a region name"); @@ -635,6 +634,39 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // Used in a closure. (LaterUseKind::ClosureCapture, capture_kind_span, Some(path_span)) } + // In the case that the borrowed value (probably a temporary) + // overlaps with the method's receiver, then point at the method. + UseSpans::FnSelfUse { + var_span: span, + kind: CallKind::Normal { desugaring: None, .. }, + .. + } if span + .overlaps(self.body.local_decls[borrow.assigned_place.local].source_info.span) => + { + if let TerminatorKind::Call { func, call_source: CallSource::Normal, .. } = + &self.body.basic_blocks[location.block].terminator().kind + { + // Just point to the function, to reduce the chance of overlapping spans. + let function_span = match func { + Operand::Constant(c) => c.span, + Operand::Copy(place) | Operand::Move(place) => { + if let Some(l) = place.as_local() { + let local_decl = &self.body.local_decls[l]; + if self.local_names[l].is_none() { + local_decl.source_info.span + } else { + span + } + } else { + span + } + } + }; + (LaterUseKind::Call, function_span, None) + } else { + (LaterUseKind::Other, span, None) + } + } UseSpans::PatUse(span) | UseSpans::OtherUse(span) | UseSpans::FnSelfUse { var_span: span, .. } => { diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 180046ca2562..d9d9ea75994f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -4,18 +4,20 @@ use std::collections::BTreeMap; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::{Applicability, Diag, MultiSpan}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan}; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::{self as hir, CoroutineKind, LangItem}; use rustc_index::IndexSlice; -use rustc_infer::infer::BoundRegionConversionTime; +use rustc_infer::infer::{ + BoundRegionConversionTime, NllRegionVariableOrigin, RegionVariableOrigin, +}; use rustc_infer::traits::SelectionError; use rustc_middle::bug; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::{ - AggregateKind, CallSource, ConstOperand, FakeReadCause, Local, LocalInfo, LocalKind, Location, - Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, - TerminatorKind, + AggregateKind, CallSource, ConstOperand, ConstraintCategory, FakeReadCause, Local, LocalInfo, + LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, + StatementKind, Terminator, TerminatorKind, }; use rustc_middle::ty::print::Print; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; @@ -33,7 +35,9 @@ use tracing::debug; use super::MirBorrowckCtxt; use super::borrow_set::BorrowData; +use crate::constraints::OutlivesConstraint; use crate::fluent_generated as fluent; +use crate::nll::ConstraintDescription; use crate::session_diagnostics::{ CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause, CaptureVarKind, CaptureVarPathUseCause, OnClosureNote, @@ -619,6 +623,52 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { region.print(&mut printer).unwrap(); printer.into_buffer() } + + /// Add a note to region errors and borrow explanations when higher-ranked regions in predicates + /// implicitly introduce an "outlives `'static`" constraint. + fn add_placeholder_from_predicate_note( + &self, + err: &mut Diag<'_, G>, + path: &[OutlivesConstraint<'tcx>], + ) { + let predicate_span = path.iter().find_map(|constraint| { + let outlived = constraint.sub; + if let Some(origin) = self.regioncx.var_infos.get(outlived) + && let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(_)) = + origin.origin + && let ConstraintCategory::Predicate(span) = constraint.category + { + Some(span) + } else { + None + } + }); + + if let Some(span) = predicate_span { + err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime"); + } + } + + /// Add a label to region errors and borrow explanations when outlives constraints arise from + /// proving a type implements `Sized` or `Copy`. + fn add_sized_or_copy_bound_info( + &self, + err: &mut Diag<'_, G>, + blamed_category: ConstraintCategory<'tcx>, + path: &[OutlivesConstraint<'tcx>], + ) { + for sought_category in [ConstraintCategory::SizedBound, ConstraintCategory::CopyBound] { + if sought_category != blamed_category + && let Some(sought_constraint) = path.iter().find(|c| c.category == sought_category) + { + let label = format!( + "requirement occurs due to {}", + sought_category.description().trim_end() + ); + err.span_label(sought_constraint.span, label); + } + } + } } /// The span(s) associated to a use of a place. @@ -661,9 +711,6 @@ impl UseSpans<'_> { UseSpans::ClosureUse { args_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { - fn_call_span - } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -674,9 +721,6 @@ impl UseSpans<'_> { UseSpans::ClosureUse { path_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { - fn_call_span - } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -687,9 +731,6 @@ impl UseSpans<'_> { UseSpans::ClosureUse { capture_kind_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { - fn_call_span - } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -1001,6 +1042,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { kind, }; } + normal_ret } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 16de160cae58..a6ca038282d9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -10,6 +10,7 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, BindingMode, ByRef, Node}; use rustc_middle::bug; use rustc_middle::hir::place::PlaceBase; +use rustc_middle::mir::visit::PlaceContext; use rustc_middle::mir::{ self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location, Mutability, Place, PlaceRef, ProjectionElem, @@ -22,7 +23,6 @@ use rustc_trait_selection::traits; use tracing::debug; use crate::diagnostics::BorrowedContentSource; -use crate::util::FindAssignments; use crate::{MirBorrowckCtxt, session_diagnostics}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -1088,6 +1088,38 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } + /// Finds all statements that assign directly to local (i.e., X = ...) and returns their + /// locations. + fn find_assignments(&self, local: Local) -> Vec { + use rustc_middle::mir::visit::Visitor; + + struct FindLocalAssignmentVisitor { + needle: Local, + locations: Vec, + } + + impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor { + fn visit_local( + &mut self, + local: Local, + place_context: PlaceContext, + location: Location, + ) { + if self.needle != local { + return; + } + + if place_context.is_place_assignment() { + self.locations.push(location); + } + } + } + + let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] }; + visitor.visit_body(self.body); + visitor.locations + } + fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) { let local_decl = &self.body.local_decls[local]; @@ -1121,7 +1153,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { })) => { // check if the RHS is from desugaring let opt_assignment_rhs_span = - self.body.find_assignments(local).first().map(|&location| { + self.find_assignments(local).first().map(|&location| { if let Some(mir::Statement { source_info: _, kind: diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 3555009c63f4..f0baa20648cd 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -13,7 +13,7 @@ use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate}; use rustc_infer::infer::{NllRegionVariableOrigin, RelateParamBound}; use rustc_middle::bug; use rustc_middle::hir::place::PlaceBase; -use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; +use rustc_middle::mir::{AnnotationSource, ConstraintCategory, ReturnConstraint}; use rustc_middle::ty::{self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeVisitor}; use rustc_span::{Ident, Span, kw}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; @@ -29,7 +29,7 @@ use tracing::{debug, instrument, trace}; use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource}; use crate::nll::ConstraintDescription; use crate::region_infer::values::RegionElement; -use crate::region_infer::{BlameConstraint, ExtraConstraintInfo, TypeTest}; +use crate::region_infer::{BlameConstraint, TypeTest}; use crate::session_diagnostics::{ FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr, LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote, @@ -49,8 +49,8 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { ConstraintCategory::Cast { is_implicit_coercion: false, .. } => "cast ", ConstraintCategory::Cast { is_implicit_coercion: true, .. } => "coercion ", ConstraintCategory::CallArgument(_) => "argument ", - ConstraintCategory::TypeAnnotation => "type annotation ", - ConstraintCategory::ClosureBounds => "closure body ", + ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg) => "generic argument ", + ConstraintCategory::TypeAnnotation(_) => "type annotation ", ConstraintCategory::SizedBound => "proving this value is `Sized` ", ConstraintCategory::CopyBound => "copying this value ", ConstraintCategory::OpaqueType => "opaque type ", @@ -440,10 +440,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ) { debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr); - let (blame_constraint, extra_info) = - self.regioncx.best_blame_constraint(fr, fr_origin, |r| { - self.regioncx.provides_universal_region(r, fr, outlived_fr) - }); + let (blame_constraint, path) = self.regioncx.best_blame_constraint(fr, fr_origin, |r| { + self.regioncx.provides_universal_region(r, fr, outlived_fr) + }); let BlameConstraint { category, cause, variance_info, .. } = blame_constraint; debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info); @@ -554,13 +553,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - for extra in extra_info { - match extra { - ExtraConstraintInfo::PlaceholderFromPredicate(span) => { - diag.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime"); - } - } - } + self.add_placeholder_from_predicate_note(&mut diag, &path); + self.add_sized_or_copy_bound_info(&mut diag, category, &path); self.buffer_error(diag); } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index bdb880b2bced..9349b46ec5b0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Display}; use std::iter; use rustc_data_structures::fx::IndexEntry; -use rustc_errors::Diag; +use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_middle::ty::print::RegionHighlightMode; @@ -108,7 +108,7 @@ impl RegionName { } } - pub(crate) fn highlight_region_name(&self, diag: &mut Diag<'_>) { + pub(crate) fn highlight_region_name(&self, diag: &mut Diag<'_, G>) { match &self.source { RegionNameSource::NamedLateParamRegion(span) | RegionNameSource::NamedEarlyParamRegion(span) => { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index b061a450c83f..f90f75141026 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -6,6 +6,7 @@ #![feature(assert_matches)] #![feature(box_patterns)] #![feature(file_buffered)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_attrs)] @@ -15,14 +16,17 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end +use std::borrow::Cow; use std::cell::RefCell; use std::marker::PhantomData; -use std::ops::Deref; +use std::ops::{ControlFlow, Deref}; use rustc_abi::FieldIdx; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::dominators::Dominators; +use rustc_errors::LintDiagnostic; use rustc_hir as hir; +use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::LocalDefId; use rustc_index::bit_set::{BitSet, MixedBitSet}; use rustc_index::{IndexSlice, IndexVec}; @@ -42,7 +46,7 @@ use rustc_mir_dataflow::move_paths::{ InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex, }; use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results}; -use rustc_session::lint::builtin::UNUSED_MUT; +use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT}; use rustc_span::{Span, Symbol}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -81,7 +85,6 @@ mod session_diagnostics; mod type_check; mod universal_regions; mod used_muts; -mod util; /// A public API provided for the Rust compiler consumers. pub mod consumers; @@ -636,9 +639,11 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt< | StatementKind::Coverage(..) // These do not actually affect borrowck | StatementKind::ConstEvalCounter - // This do not affect borrowck - | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::StorageLive(..) => {} + // This does not affect borrowck + StatementKind::BackwardIncompatibleDropHint { place, reason: BackwardIncompatibleDropReason::Edition2024 } => { + self.check_backward_incompatible_drop(location, (**place, span), state); + } StatementKind::StorageDead(local) => { self.access_place( location, @@ -1007,6 +1012,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } } + fn borrows_in_scope<'s>( + &self, + location: Location, + state: &'s BorrowckDomain, + ) -> Cow<'s, BitSet> { + if let Some(polonius) = &self.polonius_output { + // Use polonius output if it has been enabled. + let location = self.location_table.start_index(location); + let mut polonius_output = BitSet::new_empty(self.borrow_set.len()); + for &idx in polonius.errors_at(location) { + polonius_output.insert(idx); + } + Cow::Owned(polonius_output) + } else { + Cow::Borrowed(&state.borrows) + } + } + #[instrument(level = "debug", skip(self, state))] fn check_access_for_conflict( &mut self, @@ -1018,18 +1041,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ) -> bool { let mut error_reported = false; - // Use polonius output if it has been enabled. - let mut polonius_output; - let borrows_in_scope = if let Some(polonius) = &self.polonius_output { - let location = self.location_table.start_index(location); - polonius_output = BitSet::new_empty(self.borrow_set.len()); - for &idx in polonius.errors_at(location) { - polonius_output.insert(idx); - } - &polonius_output - } else { - &state.borrows - }; + let borrows_in_scope = self.borrows_in_scope(location, state); each_borrow_involving_path( self, @@ -1054,31 +1066,31 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { rw, (borrow_index, borrow), ); - Control::Continue + ControlFlow::Continue(()) } (Read(_), BorrowKind::Shared | BorrowKind::Fake(_)) | ( Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))), BorrowKind::Mut { .. }, - ) => Control::Continue, + ) => ControlFlow::Continue(()), (Reservation(_), BorrowKind::Fake(_) | BorrowKind::Shared) => { // This used to be a future compatibility warning (to be // disallowed on NLL). See rust-lang/rust#56254 - Control::Continue + ControlFlow::Continue(()) } (Write(WriteKind::Move), BorrowKind::Fake(FakeBorrowKind::Shallow)) => { // Handled by initialization checks. - Control::Continue + ControlFlow::Continue(()) } (Read(kind), BorrowKind::Mut { .. }) => { // Reading from mere reservations of mutable-borrows is OK. if !is_active(this.dominators(), borrow, location) { - assert!(allow_two_phase_borrow(borrow.kind)); - return Control::Continue; + assert!(borrow.kind.allows_two_phase_borrow()); + return ControlFlow::Continue(()); } error_reported = true; @@ -1094,7 +1106,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { this.buffer_error(err); } } - Control::Break + ControlFlow::Break(()) } (Reservation(kind) | Activation(kind, _) | Write(kind), _) => { @@ -1141,7 +1153,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { this.report_illegal_mutation_of_borrowed(location, place_span, borrow) } } - Control::Break + ControlFlow::Break(()) } }, ); @@ -1149,6 +1161,61 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { error_reported } + /// Through #123739, backward incompatible drops (BIDs) are introduced. + /// We would like to emit lints whether borrow checking fails at these future drop locations. + #[instrument(level = "debug", skip(self, state))] + fn check_backward_incompatible_drop( + &mut self, + location: Location, + (place, place_span): (Place<'tcx>, Span), + state: &BorrowckDomain, + ) { + let tcx = self.infcx.tcx; + // If this type does not need `Drop`, then treat it like a `StorageDead`. + // This is needed because we track the borrows of refs to thread locals, + // and we'll ICE because we don't track borrows behind shared references. + let sd = if place.ty(self.body, tcx).ty.needs_drop(tcx, self.body.typing_env(tcx)) { + AccessDepth::Drop + } else { + AccessDepth::Shallow(None) + }; + + let borrows_in_scope = self.borrows_in_scope(location, state); + + // This is a very simplified version of `Self::check_access_for_conflict`. + // We are here checking on BIDs and specifically still-live borrows of data involving the BIDs. + each_borrow_involving_path( + self, + self.infcx.tcx, + self.body, + (sd, place), + self.borrow_set, + |borrow_index| borrows_in_scope.contains(borrow_index), + |this, _borrow_index, borrow| { + if matches!(borrow.kind, BorrowKind::Fake(_)) { + return ControlFlow::Continue(()); + } + let borrowed = this.retrieve_borrow_spans(borrow).var_or_use_path_span(); + let explain = this.explain_why_borrow_contains_point( + location, + borrow, + Some((WriteKind::StorageDeadOrDrop, place)), + ); + this.infcx.tcx.node_span_lint( + TAIL_EXPR_DROP_ORDER, + CRATE_HIR_ID, + borrowed, + |diag| { + session_diagnostics::TailExprDropOrder { borrowed }.decorate_lint(diag); + explain.add_explanation_to_diagnostic(&this, diag, "", None, None); + }, + ); + // We may stop at the first case + ControlFlow::Break(()) + }, + ); + } + fn mutate_place( &mut self, location: Location, @@ -1185,7 +1252,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } BorrowKind::Mut { .. } => { let wk = WriteKind::MutableBorrow(bk); - if allow_two_phase_borrow(bk) { + if bk.allows_two_phase_borrow() { (Deep, Reservation(wk)) } else { (Deep, Write(wk)) diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 968b6d383c1b..7ae1df9522fb 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -142,9 +142,9 @@ pub(crate) fn compute_regions<'a, 'tcx>( // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives // constraints. - let localized_outlives_constraints = polonius_context - .as_mut() - .map(|polonius_context| polonius_context.create_localized_constraints(&mut regioncx, body)); + let localized_outlives_constraints = polonius_context.as_mut().map(|polonius_context| { + polonius_context.create_localized_constraints(infcx.tcx, ®ioncx, body) + }); // If requested: dump NLL facts, and run legacy polonius analysis. let polonius_output = all_facts.as_ref().and_then(|all_facts| { diff --git a/compiler/rustc_borrowck/src/path_utils.rs b/compiler/rustc_borrowck/src/path_utils.rs index 12a37f56fcf9..2c94a32d369c 100644 --- a/compiler/rustc_borrowck/src/path_utils.rs +++ b/compiler/rustc_borrowck/src/path_utils.rs @@ -1,26 +1,14 @@ +use std::ops::ControlFlow; + use rustc_abi::FieldIdx; use rustc_data_structures::graph::dominators::Dominators; -use rustc_middle::mir::{BasicBlock, Body, BorrowKind, Location, Place, PlaceRef, ProjectionElem}; +use rustc_middle::mir::{BasicBlock, Body, Location, Place, PlaceRef, ProjectionElem}; use rustc_middle::ty::TyCtxt; use tracing::debug; use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation}; use crate::{AccessDepth, BorrowIndex, places_conflict}; -/// Returns `true` if the borrow represented by `kind` is -/// allowed to be split into separate Reservation and -/// Activation phases. -pub(super) fn allow_two_phase_borrow(kind: BorrowKind) -> bool { - kind.allows_two_phase_borrow() -} - -/// Control for the path borrow checking code -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(super) enum Control { - Continue, - Break, -} - /// Encapsulates the idea of iterating over every borrow that involves a particular path pub(super) fn each_borrow_involving_path<'tcx, F, I, S>( s: &mut S, @@ -31,7 +19,7 @@ pub(super) fn each_borrow_involving_path<'tcx, F, I, S>( is_candidate: I, mut op: F, ) where - F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> Control, + F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> ControlFlow<()>, I: Fn(BorrowIndex) -> bool, { let (access, place) = access_place; @@ -62,7 +50,7 @@ pub(super) fn each_borrow_involving_path<'tcx, F, I, S>( i, borrowed, place, access ); let ctrl = op(s, i, borrowed); - if ctrl == Control::Break { + if matches!(ctrl, ControlFlow::Break(_)) { return; } } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index bb6d593d0d88..3c3b93cdc0d3 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -1,3 +1,5 @@ +use std::ops::ControlFlow; + use rustc_data_structures::graph::dominators::Dominators; use rustc_middle::bug; use rustc_middle::mir::visit::Visitor; @@ -260,7 +262,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { } BorrowKind::Mut { .. } => { let wk = WriteKind::MutableBorrow(bk); - if allow_two_phase_borrow(bk) { + if bk.allows_two_phase_borrow() { (Deep, Reservation(wk)) } else { (Deep, Write(wk)) @@ -378,8 +380,8 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { // Reading from mere reservations of mutable-borrows is OK. if !is_active(this.dominators, borrow, location) { // If the borrow isn't active yet, reads don't invalidate it - assert!(allow_two_phase_borrow(borrow.kind)); - return Control::Continue; + assert!(borrow.kind.allows_two_phase_borrow()); + return ControlFlow::Continue(()); } // Unique and mutable borrows are invalidated by reads from any @@ -395,7 +397,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { this.emit_loan_invalidated_at(borrow_index, location); } } - Control::Continue + ControlFlow::Continue(()) }, ); } diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index a853ff266a11..7d0f9397021b 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -37,21 +37,20 @@ mod constraints; mod dump; pub(crate) mod legacy; mod liveness_constraints; +mod typeck_constraints; use std::collections::BTreeMap; use rustc_index::bit_set::SparseBitMatrix; -use rustc_middle::mir::{Body, Location}; -use rustc_middle::ty::RegionVid; +use rustc_middle::mir::Body; +use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_mir_dataflow::points::PointIndex; pub(crate) use self::constraints::*; pub(crate) use self::dump::dump_polonius_mir; use self::liveness_constraints::create_liveness_constraints; +use self::typeck_constraints::convert_typeck_constraints; use crate::RegionInferenceContext; -use crate::constraints::OutlivesConstraint; -use crate::region_infer::values::LivenessValues; -use crate::type_check::Locations; /// This struct holds the data needed to create the Polonius localized constraints. pub(crate) struct PoloniusContext { @@ -88,14 +87,17 @@ impl PoloniusContext { /// - encoding liveness constraints pub(crate) fn create_localized_constraints<'tcx>( &self, + tcx: TyCtxt<'tcx>, regioncx: &RegionInferenceContext<'tcx>, body: &Body<'tcx>, ) -> LocalizedOutlivesConstraintSet { let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default(); convert_typeck_constraints( + tcx, body, regioncx.liveness_constraints(), regioncx.outlives_constraints(), + regioncx.universal_regions(), &mut localized_outlives_constraints, ); @@ -117,38 +119,3 @@ impl PoloniusContext { localized_outlives_constraints } } - -/// Propagate loans throughout the subset graph at a given point (with some subtleties around the -/// location where effects start to be visible). -fn convert_typeck_constraints<'tcx>( - body: &Body<'tcx>, - liveness: &LivenessValues, - outlives_constraints: impl Iterator>, - localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet, -) { - for outlives_constraint in outlives_constraints { - match outlives_constraint.locations { - Locations::All(_) => { - // For now, turn logical constraints holding at all points into physical edges at - // every point in the graph. - // FIXME: encode this into *traversal* instead. - for (block, bb) in body.basic_blocks.iter_enumerated() { - let statement_count = bb.statements.len(); - for statement_index in 0..=statement_count { - let current_location = Location { block, statement_index }; - let current_point = liveness.point_from_location(current_location); - - localized_outlives_constraints.push(LocalizedOutlivesConstraint { - source: outlives_constraint.sup, - from: current_point, - target: outlives_constraint.sub, - to: current_point, - }); - } - } - } - - _ => {} - } - } -} diff --git a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs new file mode 100644 index 000000000000..8235b844886e --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs @@ -0,0 +1,241 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_middle::mir::{Body, Location, Statement, StatementKind, Terminator, TerminatorKind}; +use rustc_middle::ty::{TyCtxt, TypeVisitable}; +use rustc_mir_dataflow::points::PointIndex; + +use super::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; +use crate::constraints::OutlivesConstraint; +use crate::region_infer::values::LivenessValues; +use crate::type_check::Locations; +use crate::universal_regions::UniversalRegions; + +/// Propagate loans throughout the subset graph at a given point (with some subtleties around the +/// location where effects start to be visible). +pub(super) fn convert_typeck_constraints<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + liveness: &LivenessValues, + outlives_constraints: impl Iterator>, + universal_regions: &UniversalRegions<'tcx>, + localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet, +) { + for outlives_constraint in outlives_constraints { + match outlives_constraint.locations { + Locations::All(_) => { + // For now, turn logical constraints holding at all points into physical edges at + // every point in the graph. + // FIXME: encode this into *traversal* instead. + for (block, bb) in body.basic_blocks.iter_enumerated() { + let statement_count = bb.statements.len(); + for statement_index in 0..=statement_count { + let current_location = Location { block, statement_index }; + let current_point = liveness.point_from_location(current_location); + + localized_outlives_constraints.push(LocalizedOutlivesConstraint { + source: outlives_constraint.sup, + from: current_point, + target: outlives_constraint.sub, + to: current_point, + }); + } + } + } + + Locations::Single(location) => { + // This constraint is marked as holding at one location, we localize it to that + // location or its successor, depending on the corresponding MIR + // statement/terminator. Unfortunately, they all show up from typeck as coming "on + // entry", so for now we modify them to take effects that should apply "on exit" + // into account. + // + // FIXME: this approach is subtle, complicated, and hard to test, so we should track + // this information better in MIR typeck instead, for example with a new `Locations` + // variant that contains which node is crossing over between entry and exit. + let point = liveness.point_from_location(location); + let localized_constraint = if let Some(stmt) = + body[location.block].statements.get(location.statement_index) + { + localize_statement_constraint( + tcx, + body, + stmt, + liveness, + &outlives_constraint, + location, + point, + universal_regions, + ) + } else { + assert_eq!(location.statement_index, body[location.block].statements.len()); + let terminator = body[location.block].terminator(); + localize_terminator_constraint( + tcx, + body, + terminator, + liveness, + &outlives_constraint, + point, + universal_regions, + ) + }; + localized_outlives_constraints.push(localized_constraint); + } + } + } +} + +/// For a given outlives constraint arising from a MIR statement, localize the constraint with the +/// needed CFG `from`-`to` intra-block nodes. +fn localize_statement_constraint<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + stmt: &Statement<'tcx>, + liveness: &LivenessValues, + outlives_constraint: &OutlivesConstraint<'tcx>, + current_location: Location, + current_point: PointIndex, + universal_regions: &UniversalRegions<'tcx>, +) -> LocalizedOutlivesConstraint { + match &stmt.kind { + StatementKind::Assign(box (lhs, rhs)) => { + // To create localized outlives constraints without midpoints, we rely on the property + // that no input regions from the RHS of the assignment will flow into themselves: they + // should not appear in the output regions in the LHS. We believe this to be true by + // construction of the MIR, via temporaries, and assert it here. + // + // We think we don't need midpoints because: + // - every LHS Place has a unique set of regions that don't appear elsewhere + // - this implies that for them to be part of the RHS, the same Place must be read and + // written + // - and that should be impossible in MIR + // + // When we have a more complete implementation in the future, tested with crater, etc, + // we can relax this to a debug assert instead, or remove it. + assert!( + { + let mut lhs_regions = FxHashSet::default(); + tcx.for_each_free_region(lhs, |region| { + let region = universal_regions.to_region_vid(region); + lhs_regions.insert(region); + }); + + let mut rhs_regions = FxHashSet::default(); + tcx.for_each_free_region(rhs, |region| { + let region = universal_regions.to_region_vid(region); + rhs_regions.insert(region); + }); + + // The intersection between LHS and RHS regions should be empty. + lhs_regions.is_disjoint(&rhs_regions) + }, + "there should be no common regions between the LHS and RHS of an assignment" + ); + + // As mentioned earlier, we should be tracking these better upstream but: we want to + // relate the types on entry to the type of the place on exit. That is, outlives + // constraints on the RHS are on entry, and outlives constraints to/from the LHS are on + // exit (i.e. on entry to the successor location). + let lhs_ty = body.local_decls[lhs.local].ty; + let successor_location = Location { + block: current_location.block, + statement_index: current_location.statement_index + 1, + }; + let successor_point = liveness.point_from_location(successor_location); + compute_constraint_direction( + tcx, + outlives_constraint, + &lhs_ty, + current_point, + successor_point, + universal_regions, + ) + } + _ => { + // For the other cases, we localize an outlives constraint to where it arises. + LocalizedOutlivesConstraint { + source: outlives_constraint.sup, + from: current_point, + target: outlives_constraint.sub, + to: current_point, + } + } + } +} + +/// For a given outlives constraint arising from a MIR terminator, localize the constraint with the +/// needed CFG `from`-`to` inter-block nodes. +fn localize_terminator_constraint<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + terminator: &Terminator<'tcx>, + liveness: &LivenessValues, + outlives_constraint: &OutlivesConstraint<'tcx>, + current_point: PointIndex, + universal_regions: &UniversalRegions<'tcx>, +) -> LocalizedOutlivesConstraint { + // FIXME: check if other terminators need the same handling as `Call`s, in particular + // Assert/Yield/Drop. A handful of tests are failing with Drop related issues, as well as some + // coroutine tests, and that may be why. + match &terminator.kind { + // FIXME: also handle diverging calls. + TerminatorKind::Call { destination, target: Some(target), .. } => { + // Calls are similar to assignments, and thus follow the same pattern. If there is a + // target for the call we also relate what flows into the destination here to entry to + // that successor. + let destination_ty = destination.ty(&body.local_decls, tcx); + let successor_location = Location { block: *target, statement_index: 0 }; + let successor_point = liveness.point_from_location(successor_location); + compute_constraint_direction( + tcx, + outlives_constraint, + &destination_ty, + current_point, + successor_point, + universal_regions, + ) + } + _ => { + // Typeck constraints guide loans between regions at the current point, so we do that in + // the general case, and liveness will take care of making them flow to the terminator's + // successors. + LocalizedOutlivesConstraint { + source: outlives_constraint.sup, + from: current_point, + target: outlives_constraint.sub, + to: current_point, + } + } + } +} +/// For a given outlives constraint and CFG edge, returns the localized constraint with the +/// appropriate `from`-`to` direction. This is computed according to whether the constraint flows to +/// or from a free region in the given `value`, some kind of result for an effectful operation, like +/// the LHS of an assignment. +fn compute_constraint_direction<'tcx>( + tcx: TyCtxt<'tcx>, + outlives_constraint: &OutlivesConstraint<'tcx>, + value: &impl TypeVisitable>, + current_point: PointIndex, + successor_point: PointIndex, + universal_regions: &UniversalRegions<'tcx>, +) -> LocalizedOutlivesConstraint { + let mut to = current_point; + let mut from = current_point; + tcx.for_each_free_region(value, |region| { + let region = universal_regions.to_region_vid(region); + if region == outlives_constraint.sub { + // This constraint flows into the result, its effects start becoming visible on exit. + to = successor_point; + } else if region == outlives_constraint.sup { + // This constraint flows from the result, its effects start becoming visible on exit. + from = successor_point; + } + }); + + LocalizedOutlivesConstraint { + source: outlives_constraint.sup, + from, + target: outlives_constraint.sub, + to, + } +} diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 2150759d329e..59189082f6fc 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -13,15 +13,16 @@ use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; use rustc_middle::bug; use rustc_middle::mir::{ - BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureOutlivesSubjectTy, - ClosureRegionRequirements, ConstraintCategory, Local, Location, ReturnConstraint, - TerminatorKind, + AnnotationSource, BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, + ClosureOutlivesSubjectTy, ClosureRegionRequirements, ConstraintCategory, Local, Location, + ReturnConstraint, TerminatorKind, }; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex}; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_span::Span; +use rustc_span::hygiene::DesugaringKind; use tracing::{debug, instrument, trace}; use crate::BorrowckInferCtxt; @@ -315,11 +316,6 @@ enum Trace<'tcx> { NotVisited, } -#[derive(Clone, PartialEq, Eq, Debug)] -pub(crate) enum ExtraConstraintInfo { - PlaceholderFromPredicate(Span), -} - #[instrument(skip(infcx, sccs), level = "debug")] fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) { use crate::renumber::RegionCtxt; @@ -978,7 +974,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements: &mut Vec>, ) -> bool { let tcx = infcx.tcx; - let TypeTest { generic_kind, lower_bound, span: blame_span, ref verify_bound } = *type_test; + let TypeTest { generic_kind, lower_bound, span: blame_span, verify_bound: _ } = *type_test; let generic_ty = generic_kind.to_ty(tcx); let Some(subject) = self.try_promote_type_test_subject(infcx, generic_ty) else { @@ -1016,25 +1012,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { // For each region outlived by lower_bound find a non-local, // universal region (it may be the same region) and add it to // `ClosureOutlivesRequirement`. + let mut found_outlived_universal_region = false; for ur in self.scc_values.universal_regions_outlived_by(r_scc) { + found_outlived_universal_region = true; debug!("universal_region_outlived_by ur={:?}", ur); - // Check whether we can already prove that the "subject" outlives `ur`. - // If so, we don't have to propagate this requirement to our caller. - // - // To continue the example from the function, if we are trying to promote - // a requirement that `T: 'X`, and we know that `'X = '1 + '2` (i.e., the union - // `'1` and `'2`), then in this loop `ur` will be `'1` (and `'2`). So here - // we check whether `T: '1` is something we *can* prove. If so, no need - // to propagate that requirement. - // - // This is needed because -- particularly in the case - // where `ur` is a local bound -- we are sometimes in a - // position to prove things that our caller cannot. See - // #53570 for an example. - if self.eval_verify_bound(infcx, generic_ty, ur, &verify_bound) { - continue; - } - let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur); debug!(?non_local_ub); @@ -1056,6 +1037,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements.push(requirement); } } + // If we succeed to promote the subject, i.e. it only contains non-local regions, + // and fail to prove the type test inside of the closure, the `lower_bound` has to + // also be at least as large as some universal region, as the type test is otherwise + // trivial. + assert!(found_outlived_universal_region); true } @@ -1948,7 +1934,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { from_region: RegionVid, from_region_origin: NllRegionVariableOrigin, target_test: impl Fn(RegionVid) -> bool, - ) -> (BlameConstraint<'tcx>, Vec) { + ) -> (BlameConstraint<'tcx>, Vec>) { // Find all paths let (path, target_region) = self .find_constraint_paths_between_regions(from_region, target_test) @@ -1970,25 +1956,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { .collect::>() ); - let mut extra_info = vec![]; - for constraint in path.iter() { - let outlived = constraint.sub; - let Some(origin) = self.var_infos.get(outlived) else { - continue; - }; - let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(p)) = origin.origin - else { - continue; - }; - debug!(?constraint, ?p); - let ConstraintCategory::Predicate(span) = constraint.category else { - continue; - }; - extra_info.push(ExtraConstraintInfo::PlaceholderFromPredicate(span)); - // We only want to point to one - break; - } - // We try to avoid reporting a `ConstraintCategory::Predicate` as our best constraint. // Instead, we use it to produce an improved `ObligationCauseCode`. // FIXME - determine what we should do if we encounter multiple @@ -2007,42 +1974,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { }) .unwrap_or_else(|| ObligationCauseCode::Misc); - // Classify each of the constraints along the path. - let mut categorized_path: Vec> = path - .iter() - .map(|constraint| BlameConstraint { - category: constraint.category, - from_closure: constraint.from_closure, - cause: ObligationCause::new(constraint.span, CRATE_DEF_ID, cause_code.clone()), - variance_info: constraint.variance_info, - }) - .collect(); - debug!("categorized_path={:#?}", categorized_path); - - // To find the best span to cite, we first try to look for the - // final constraint that is interesting and where the `sup` is - // not unified with the ultimate target region. The reason - // for this is that we have a chain of constraints that lead - // from the source to the target region, something like: - // - // '0: '1 ('0 is the source) - // '1: '2 - // '2: '3 - // '3: '4 - // '4: '5 - // '5: '6 ('6 is the target) - // - // Some of those regions are unified with `'6` (in the same - // SCC). We want to screen those out. After that point, the - // "closest" constraint we have to the end is going to be the - // most likely to be the point where the value escapes -- but - // we still want to screen for an "interesting" point to - // highlight (e.g., a call site or something). - let target_scc = self.constraint_sccs.scc(target_region); - let mut range = 0..path.len(); - - // As noted above, when reporting an error, there is typically a chain of constraints - // leading from some "source" region which must outlive some "target" region. + // When reporting an error, there is typically a chain of constraints leading from some + // "source" region which must outlive some "target" region. // In most cases, we prefer to "blame" the constraints closer to the target -- // but there is one exception. When constraints arise from higher-ranked subtyping, // we generally prefer to blame the source value, @@ -2083,78 +2016,114 @@ impl<'tcx> RegionInferenceContext<'tcx> { | NllRegionVariableOrigin::Existential { from_forall: true } => false, }; - let find_region = |i: &usize| { - let constraint = &path[*i]; - - let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup); - - if blame_source { - match categorized_path[*i].category { - ConstraintCategory::OpaqueType - | ConstraintCategory::Boring - | ConstraintCategory::BoringNoLocation - | ConstraintCategory::Internal - | ConstraintCategory::Predicate(_) => false, - ConstraintCategory::TypeAnnotation - | ConstraintCategory::Return(_) - | ConstraintCategory::Yield => true, - _ => constraint_sup_scc != target_scc, - } + // To pick a constraint to blame, we organize constraints by how interesting we expect them + // to be in diagnostics, then pick the most interesting one closest to either the source or + // the target on our constraint path. + let constraint_interest = |constraint: &OutlivesConstraint<'tcx>| { + // Try to avoid blaming constraints from desugarings, since they may not clearly match + // match what users have written. As an exception, allow blaming returns generated by + // `?` desugaring, since the correspondence is fairly clear. + let category = if let Some(kind) = constraint.span.desugaring_kind() + && (kind != DesugaringKind::QuestionMark + || !matches!(constraint.category, ConstraintCategory::Return(_))) + { + ConstraintCategory::Boring } else { - !matches!( - categorized_path[*i].category, - ConstraintCategory::OpaqueType - | ConstraintCategory::Boring - | ConstraintCategory::BoringNoLocation - | ConstraintCategory::Internal - | ConstraintCategory::Predicate(_) + constraint.category + }; + + match category { + // Returns usually provide a type to blame and have specially written diagnostics, + // so prioritize them. + ConstraintCategory::Return(_) => 0, + // Unsizing coercions are interesting, since we have a note for that: + // `BorrowExplanation::add_object_lifetime_default_note`. + // FIXME(dianne): That note shouldn't depend on a coercion being blamed; see issue + // #131008 for an example of where we currently don't emit it but should. + // Once the note is handled properly, this case should be removed. Until then, it + // should be as limited as possible; the note is prone to false positives and this + // constraint usually isn't best to blame. + ConstraintCategory::Cast { + unsize_to: Some(unsize_ty), + is_implicit_coercion: true, + } if target_region == self.universal_regions().fr_static + // Mirror the note's condition, to minimize how often this diverts blame. + && let ty::Adt(_, args) = unsize_ty.kind() + && args.iter().any(|arg| arg.as_type().is_some_and(|ty| ty.is_trait())) + // Mimic old logic for this, to minimize false positives in tests. + && !path + .iter() + .any(|c| matches!(c.category, ConstraintCategory::TypeAnnotation(_))) => + { + 1 + } + // Between other interesting constraints, order by their position on the `path`. + ConstraintCategory::Yield + | ConstraintCategory::UseAsConst + | ConstraintCategory::UseAsStatic + | ConstraintCategory::TypeAnnotation( + AnnotationSource::Ascription + | AnnotationSource::Declaration + | AnnotationSource::OpaqueCast, ) + | ConstraintCategory::Cast { .. } + | ConstraintCategory::CallArgument(_) + | ConstraintCategory::CopyBound + | ConstraintCategory::SizedBound + | ConstraintCategory::Assignment + | ConstraintCategory::Usage + | ConstraintCategory::ClosureUpvar(_) => 2, + // Generic arguments are unlikely to be what relates regions together + ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg) => 3, + // We handle predicates and opaque types specially; don't prioritize them here. + ConstraintCategory::Predicate(_) | ConstraintCategory::OpaqueType => 4, + // `Boring` constraints can correspond to user-written code and have useful spans, + // but don't provide any other useful information for diagnostics. + ConstraintCategory::Boring => 5, + // `BoringNoLocation` constraints can point to user-written code, but are less + // specific, and are not used for relations that would make sense to blame. + ConstraintCategory::BoringNoLocation => 6, + // Do not blame internal constraints. + ConstraintCategory::Internal => 7, + ConstraintCategory::IllegalUniverse => 8, } }; - let best_choice = - if blame_source { range.rev().find(find_region) } else { range.find(find_region) }; + let best_choice = if blame_source { + path.iter().enumerate().rev().min_by_key(|(_, c)| constraint_interest(c)).unwrap().0 + } else { + path.iter().enumerate().min_by_key(|(_, c)| constraint_interest(c)).unwrap().0 + }; - debug!(?best_choice, ?blame_source, ?extra_info); + debug!(?best_choice, ?blame_source); - if let Some(i) = best_choice { - if let Some(next) = categorized_path.get(i + 1) { - if matches!(categorized_path[i].category, ConstraintCategory::Return(_)) - && next.category == ConstraintCategory::OpaqueType - { - // The return expression is being influenced by the return type being - // impl Trait, point at the return type and not the return expr. - return (next.clone(), extra_info); - } + let best_constraint = if let Some(next) = path.get(best_choice + 1) + && matches!(path[best_choice].category, ConstraintCategory::Return(_)) + && next.category == ConstraintCategory::OpaqueType + { + // The return expression is being influenced by the return type being + // impl Trait, point at the return type and not the return expr. + *next + } else if path[best_choice].category == ConstraintCategory::Return(ReturnConstraint::Normal) + && let Some(field) = path.iter().find_map(|p| { + if let ConstraintCategory::ClosureUpvar(f) = p.category { Some(f) } else { None } + }) + { + OutlivesConstraint { + category: ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)), + ..path[best_choice] } + } else { + path[best_choice] + }; - if categorized_path[i].category == ConstraintCategory::Return(ReturnConstraint::Normal) - { - let field = categorized_path.iter().find_map(|p| { - if let ConstraintCategory::ClosureUpvar(f) = p.category { - Some(f) - } else { - None - } - }); - - if let Some(field) = field { - categorized_path[i].category = - ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)); - } - } - - return (categorized_path[i].clone(), extra_info); - } - - // If that search fails, that is.. unusual. Maybe everything - // is in the same SCC or something. In that case, find what - // appears to be the most interesting point to report to the - // user via an even more ad-hoc guess. - categorized_path.sort_by_key(|p| p.category); - debug!("sorted_path={:#?}", categorized_path); - - (categorized_path.remove(0), extra_info) + let blame_constraint = BlameConstraint { + category: best_constraint.category, + from_closure: best_constraint.from_closure, + cause: ObligationCause::new(best_constraint.span, CRATE_DEF_ID, cause_code.clone()), + variance_info: best_constraint.variance_info, + }; + (blame_constraint, path) } pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index e567f3a8b0de..75aef8b303ba 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -544,12 +544,12 @@ fn pretty_print_region_elements(elements: impl IntoIterator Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { context.ambient_variance(), base_ty.ty, location.to_locations(), - ConstraintCategory::TypeAnnotation, + ConstraintCategory::TypeAnnotation(AnnotationSource::OpaqueCast), ) .unwrap(); } @@ -333,7 +333,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { ty::Invariant, &UserTypeProjection { base: annotation_index, projs: vec![] }, locations, - ConstraintCategory::Boring, + ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg), ) { let annotation = &self.typeck.user_type_annotations[annotation_index]; span_mirbug!( @@ -455,7 +455,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { ty::Invariant, user_ty, Locations::All(*span), - ConstraintCategory::TypeAnnotation, + ConstraintCategory::TypeAnnotation(AnnotationSource::Declaration), ) { span_mirbug!( self, @@ -892,6 +892,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Some(l) if !body.local_decls[l].is_user_variable() => { ConstraintCategory::Boring } + Some(_) + if let Some(body_id) = tcx + .hir_node_by_def_id(body.source.def_id().expect_local()) + .body_id() + && let params = tcx.hir().body(body_id).params + && params + .iter() + .any(|param| param.span.contains(stmt.source_info.span)) => + { + // Assignments generated from lowering argument patterns shouldn't be called + // "assignments" in diagnostics and aren't interesting to blame for errors. + ConstraintCategory::Boring + } _ => ConstraintCategory::Assignment, }; debug!( @@ -927,7 +940,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ty::Invariant, &UserTypeProjection { base: annotation_index, projs: vec![] }, location.to_locations(), - ConstraintCategory::Boring, + ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg), ) { let annotation = &self.user_type_annotations[annotation_index]; span_mirbug!( @@ -962,7 +975,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *variance, projection, Locations::All(stmt.source_info.span), - ConstraintCategory::TypeAnnotation, + ConstraintCategory::TypeAnnotation(AnnotationSource::Ascription), ) { let annotation = &self.user_type_annotations[projection.base]; span_mirbug!( @@ -1226,6 +1239,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Some(l) if !body.local_decls[l].is_user_variable() => { ConstraintCategory::Boring } + // The return type of a call is interesting for diagnostics. _ => ConstraintCategory::Assignment, }; @@ -2169,7 +2183,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ty_left, common_ty, location.to_locations(), - ConstraintCategory::Boring, + ConstraintCategory::CallArgument(None), ) .unwrap_or_else(|err| { bug!("Could not equate type variable with {:?}: {:?}", ty_left, err) @@ -2178,7 +2192,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ty_right, common_ty, location.to_locations(), - ConstraintCategory::Boring, + ConstraintCategory::CallArgument(None), ) { span_mirbug!( self, diff --git a/compiler/rustc_borrowck/src/util/collect_writes.rs b/compiler/rustc_borrowck/src/util/collect_writes.rs deleted file mode 100644 index 55f1073176ae..000000000000 --- a/compiler/rustc_borrowck/src/util/collect_writes.rs +++ /dev/null @@ -1,35 +0,0 @@ -use rustc_middle::mir::visit::{PlaceContext, Visitor}; -use rustc_middle::mir::{Body, Local, Location}; - -pub(crate) trait FindAssignments { - // Finds all statements that assign directly to local (i.e., X = ...) - // and returns their locations. - fn find_assignments(&self, local: Local) -> Vec; -} - -impl<'tcx> FindAssignments for Body<'tcx> { - fn find_assignments(&self, local: Local) -> Vec { - let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] }; - visitor.visit_body(self); - visitor.locations - } -} - -// The Visitor walks the MIR to return the assignment statements corresponding -// to a Local. -struct FindLocalAssignmentVisitor { - needle: Local, - locations: Vec, -} - -impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor { - fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) { - if self.needle != local { - return; - } - - if place_context.is_place_assignment() { - self.locations.push(location); - } - } -} diff --git a/compiler/rustc_borrowck/src/util/mod.rs b/compiler/rustc_borrowck/src/util/mod.rs deleted file mode 100644 index 5f2960b768b2..000000000000 --- a/compiler/rustc_borrowck/src/util/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod collect_writes; - -pub(crate) use collect_writes::FindAssignments; diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 2ee94146c1a4..a8333df77e6e 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -56,11 +56,6 @@ jobs: - os: macos-latest env: TARGET_TRIPLE: x86_64-apple-darwin - # cross-compile from Linux to Windows using mingw - - os: ubuntu-latest - env: - TARGET_TRIPLE: x86_64-pc-windows-gnu - apt_deps: gcc-mingw-w64-x86-64 wine-stable - os: ubuntu-latest env: TARGET_TRIPLE: aarch64-unknown-linux-gnu @@ -113,15 +108,6 @@ jobs: - name: Prepare dependencies run: ./y.sh prepare - # The Wine version shipped with Ubuntu 22.04 doesn't implement bcryptprimitives.dll - - name: Build bcryptprimitives.dll shim for Wine - if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' - run: | - rustup target add x86_64-pc-windows-gnu - mkdir wine_shims - rustc patches/bcryptprimitives.rs -Copt-level=3 -Clto=fat --out-dir wine_shims --target x86_64-pc-windows-gnu - echo "WINEPATH=$(pwd)/wine_shims" >> $GITHUB_ENV - - name: Build run: ./y.sh build --sysroot none @@ -135,9 +121,6 @@ jobs: # This is roughly config rust-lang/rust uses for testing - name: Test with LLVM sysroot - # Skip native x86_64-pc-windows-gnu. It is way too slow and cross-compiled - # x86_64-pc-windows-gnu covers at least part of the tests. - if: matrix.os != 'windows-latest' || matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu' env: TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }} run: ./y.sh test --sysroot llvm --no-unstable-features @@ -215,10 +198,6 @@ jobs: - os: macos-latest env: TARGET_TRIPLE: aarch64-apple-darwin - # cross-compile from Linux to Windows using mingw - - os: ubuntu-latest - env: - TARGET_TRIPLE: x86_64-pc-windows-gnu - os: windows-latest env: TARGET_TRIPLE: x86_64-pc-windows-msvc @@ -243,12 +222,6 @@ jobs: if: matrix.os == 'macos-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-apple-darwin' run: rustup set default-host x86_64-apple-darwin - - name: Install MinGW toolchain - if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' - run: | - sudo apt-get update - sudo apt-get install -y gcc-mingw-w64-x86-64 - - name: Prepare dependencies run: ./y.sh prepare @@ -262,19 +235,11 @@ jobs: run: tar cvfJ cg_clif.tar.xz dist - name: Upload prebuilt cg_clif - if: matrix.os == 'windows-latest' || matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu' uses: actions/upload-artifact@v4 with: name: cg_clif-${{ matrix.env.TARGET_TRIPLE }} path: cg_clif.tar.xz - - name: Upload prebuilt cg_clif (cross compile) - if: matrix.os != 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' - uses: actions/upload-artifact@v4 - with: - name: cg_clif-${{ runner.os }}-cross-x86_64-mingw - path: cg_clif.tar.xz - release: runs-on: ubuntu-latest timeout-minutes: 10 diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index d81e7214961f..ec71370ef9e7 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -3,22 +3,16 @@ version = 4 [[package]] -name = "ahash" -version = "0.8.11" +name = "allocator-api2" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "arbitrary" @@ -37,6 +31,9 @@ name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +dependencies = [ + "allocator-api2", +] [[package]] name = "cfg-if" @@ -46,24 +43,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba4f80548f22dc9c43911907b5e322c5555544ee85f785115701e6a28c9abe1" +checksum = "ac89549be94911dd0e839b4a7db99e9ed29c17517e1c026f61066884c168aa3c" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005884e3649c3e5ff2dc79e8a94b138f11569cc08a91244a292714d2a86e9156" +checksum = "b9bd49369f76c77e34e641af85d0956869237832c118964d08bf5f51f210875a" [[package]] name = "cranelift-codegen" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4036255ec33ce9a37495dfbcfc4e1118fd34e693eff9a1e106336b7cd16a9b" +checksum = "fd96ce9cf8efebd7f5ab8ced5a0ce44250280bbae9f593d74a6d7effc3582a35" dependencies = [ "bumpalo", "cranelift-bforest", @@ -74,7 +71,7 @@ dependencies = [ "cranelift-entity", "cranelift-isle", "gimli", - "hashbrown", + "hashbrown 0.14.5", "log", "regalloc2", "rustc-hash", @@ -85,42 +82,42 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ca74f4b68319da11d39e894437cb6e20ec7c2e11fbbda823c3bf207beedff7" +checksum = "5a68e358827afe4bfb6239fcbf6fbd5ac56206ece8a99c8f5f9bbd518773281a" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e54f433a0269c4187871aa06d452214d5515d228d5bdc22219585e9eef895" +checksum = "e184c9767afbe73d50c55ec29abcf4c32f9baf0d9d22b86d58c4d55e06dee181" [[package]] name = "cranelift-control" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cb4018f5bf59fb53f515fa9d80e6f8c5ce19f198dc538984ebd23ecf8965ec" +checksum = "5cc7664f2a66f053e33f149e952bb5971d138e3af637f5097727ed6dc0ed95dd" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305399fd781a2953ac78c1396f02ff53144f39c33eb7fc7789cf4e8936d13a96" +checksum = "118597e3a9cf86c3556fa579a7a23b955fa18231651a52a77a2475d305a9cf84" dependencies = [ "cranelift-bitset", ] [[package]] name = "cranelift-frontend" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9230b460a128d53653456137751d27baf567947a3ab8c0c4d6e31fd08036d81e" +checksum = "7638ea1efb069a0aa18d8ee67401b6b0d19f6bfe5de5e9ede348bfc80bb0d8c7" dependencies = [ "cranelift-codegen", "log", @@ -130,15 +127,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b961e24ae3ec9813a24a15ae64bbd2a42e4de4d79a7f3225a412e3b94e78d1c8" +checksum = "15c53e1152a0b01c4ed2b1e0535602b8e86458777dd9d18b28732b16325c7dc0" [[package]] name = "cranelift-jit" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62699329d4ced20fe281fbaef45e11b473b7ab310491b4bdebcd8b818a8ef7fe" +checksum = "36972cab12ff246afe8d45b6a427669cf814bd393c661e5e8a8dedc26a81c73f" dependencies = [ "anyhow", "cranelift-codegen", @@ -156,9 +153,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f20b0b51ba962dac30fc7e812b86e4390d908acd4f59bcc8ac7610a8f3e0977" +checksum = "11841b3f54ac480db1e8e8d5678ba901a13b387012d315e3f8fba3e7b7a80447" dependencies = [ "anyhow", "cranelift-codegen", @@ -167,9 +164,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5bd76df6c9151188dfa428c863b33da5b34561b67f43c0cf3f24a794f9fa1f" +checksum = "7b7d8f895444fa52dd7bdd0bed11bf007a7fb43af65a6deac8fcc4094c6372f7" dependencies = [ "cranelift-codegen", "libc", @@ -178,9 +175,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee231640a7ecceedd0f1f2782d9288db6a6908cc70675ed9427e3bf0ea6daacd" +checksum = "8e235ddfd19f100855ad03358c7ae0a13070c38a000701054cab46458cca6e81" dependencies = [ "anyhow", "cranelift-codegen", @@ -212,6 +209,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "gimli" version = "0.31.1" @@ -228,18 +231,24 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "ahash", + "foldhash", ] [[package]] name = "indexmap" -version = "2.2.6" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.2", ] [[package]] @@ -281,27 +290,21 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "object" -version = "0.36.2" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "crc32fast", - "hashbrown", + "hashbrown 0.15.2", "indexmap", "memchr", ] -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -317,14 +320,15 @@ dependencies = [ [[package]] name = "regalloc2" -version = "0.10.2" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12908dbeb234370af84d0579b9f68258a0f67e201412dd9a2814e6f45b2fc0f0" +checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3" dependencies = [ - "hashbrown", + "allocator-api2", + "bumpalo", + "hashbrown 0.15.2", "log", "rustc-hash", - "slice-group-by", "smallvec", ] @@ -366,30 +370,24 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "slice-group-by" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" - [[package]] name = "smallvec" version = "1.13.2" @@ -404,9 +402,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" -version = "2.0.70" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -425,17 +423,11 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - [[package]] name = "wasmtime-jit-icache-coherence" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b218a92866f74f35162f5d03a4e0f62cd0e1cc624285b1014275e5d4575fad" +checksum = "d40d7722b9e1fbeae135715710a8a2570b1e6cf72b74dd653962d89831c6c70d" dependencies = [ "anyhow", "cfg-if", @@ -524,23 +516,3 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index b2fed3c490ed..82d2b6cb2c4c 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,12 +8,12 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.114.0", default-features = false, features = ["std", "unwind", "all-native-arch"] } -cranelift-frontend = { version = "0.114.0" } -cranelift-module = { version = "0.114.0" } -cranelift-native = { version = "0.114.0" } -cranelift-jit = { version = "0.114.0", optional = true } -cranelift-object = { version = "0.114.0" } +cranelift-codegen = { version = "0.115.0", default-features = false, features = ["std", "unwind", "all-native-arch"] } +cranelift-frontend = { version = "0.115.0" } +cranelift-module = { version = "0.115.0" } +cranelift-native = { version = "0.115.0" } +cranelift-jit = { version = "0.115.0", optional = true } +cranelift-object = { version = "0.115.0" } target-lexicon = "0.12.0" gimli = { version = "0.31", default-features = false, features = ["write"] } object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } diff --git a/compiler/rustc_codegen_cranelift/build_system/bench.rs b/compiler/rustc_codegen_cranelift/build_system/bench.rs index 73a0f325fc21..8359b7b52790 100644 --- a/compiler/rustc_codegen_cranelift/build_system/bench.rs +++ b/compiler/rustc_codegen_cranelift/build_system/bench.rs @@ -16,11 +16,7 @@ static SIMPLE_RAYTRACER_REPO: GitRepo = GitRepo::github( "", ); -pub(crate) fn benchmark(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { - benchmark_simple_raytracer(dirs, bootstrap_host_compiler); -} - -fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { +pub(crate) fn benchmark(dirs: &Dirs, compiler: &Compiler) { if std::process::Command::new("hyperfine").output().is_err() { eprintln!("Hyperfine not installed"); eprintln!("Hint: Try `cargo install hyperfine` to install hyperfine"); @@ -39,9 +35,9 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { }; eprintln!("[BENCH COMPILE] ebobby/simple-raytracer"); - let cargo_clif = dirs - .dist_dir - .join(get_file_name(&bootstrap_host_compiler.rustc, "cargo_clif", "bin").replace('_', "-")); + let cargo_clif = &compiler.cargo; + let rustc_clif = &compiler.rustc; + let rustflags = &compiler.rustflags.join("\x1f"); let manifest_path = SIMPLE_RAYTRACER_REPO.source_dir().to_path(dirs).join("Cargo.toml"); let target_dir = dirs.build_dir.join("simple_raytracer"); @@ -56,22 +52,24 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { target_dir = target_dir.display(), ); let clif_build_cmd = format!( - "RUSTC=rustc {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} && (rm build/raytracer_cg_clif || true) && ln build/simple_raytracer/debug/main build/raytracer_cg_clif", + "RUSTC={rustc_clif} CARGO_ENCODED_RUSTFLAGS=\"{rustflags}\" {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} && (rm build/raytracer_cg_clif || true) && ln build/simple_raytracer/debug/main build/raytracer_cg_clif", cargo_clif = cargo_clif.display(), + rustc_clif = rustc_clif.display(), manifest_path = manifest_path.display(), target_dir = target_dir.display(), ); let clif_build_opt_cmd = format!( - "RUSTC=rustc {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} --release && (rm build/raytracer_cg_clif_opt || true) && ln build/simple_raytracer/release/main build/raytracer_cg_clif_opt", + "RUSTC={rustc_clif} CARGO_ENCODED_RUSTFLAGS=\"{rustflags}\" {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} --release && (rm build/raytracer_cg_clif_opt || true) && ln build/simple_raytracer/release/main build/raytracer_cg_clif_opt", cargo_clif = cargo_clif.display(), + rustc_clif = rustc_clif.display(), manifest_path = manifest_path.display(), target_dir = target_dir.display(), ); - let bench_compile_markdown = dirs.dist_dir.join("bench_compile.md"); + let bench_compile_markdown = dirs.build_dir.join("bench_compile.md"); let bench_compile = hyperfine_command( - 1, + 0, bench_runs, Some(&clean_cmd), &[ @@ -92,23 +90,14 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { eprintln!("[BENCH RUN] ebobby/simple-raytracer"); - let bench_run_markdown = dirs.dist_dir.join("bench_run.md"); + let bench_run_markdown = dirs.build_dir.join("bench_run.md"); - let raytracer_cg_llvm = Path::new(".").join(get_file_name( - &bootstrap_host_compiler.rustc, - "raytracer_cg_llvm", - "bin", - )); - let raytracer_cg_clif = Path::new(".").join(get_file_name( - &bootstrap_host_compiler.rustc, - "raytracer_cg_clif", - "bin", - )); - let raytracer_cg_clif_opt = Path::new(".").join(get_file_name( - &bootstrap_host_compiler.rustc, - "raytracer_cg_clif_opt", - "bin", - )); + let raytracer_cg_llvm = + Path::new(".").join(get_file_name(&compiler.rustc, "raytracer_cg_llvm", "bin")); + let raytracer_cg_clif = + Path::new(".").join(get_file_name(&compiler.rustc, "raytracer_cg_clif", "bin")); + let raytracer_cg_clif_opt = + Path::new(".").join(get_file_name(&compiler.rustc, "raytracer_cg_clif_opt", "bin")); let mut bench_run = hyperfine_command( 0, bench_runs, diff --git a/compiler/rustc_codegen_cranelift/build_system/config.rs b/compiler/rustc_codegen_cranelift/build_system/config.rs index ef540cf1f822..37bc4c5d7827 100644 --- a/compiler/rustc_codegen_cranelift/build_system/config.rs +++ b/compiler/rustc_codegen_cranelift/build_system/config.rs @@ -33,23 +33,3 @@ pub(crate) fn get_bool(name: &str) -> bool { true } } - -pub(crate) fn get_value(name: &str) -> Option { - let values = load_config_file() - .into_iter() - .filter(|(key, _)| key == name) - .map(|(_, val)| val) - .collect::>(); - if values.is_empty() { - None - } else if values.len() == 1 { - if values[0].is_none() { - eprintln!("Config `{}` missing value", name); - process::exit(1); - } - values.into_iter().next().unwrap() - } else { - eprintln!("Config `{}` given multiple values: {:?}", name, values); - process::exit(1); - } -} diff --git a/compiler/rustc_codegen_cranelift/build_system/main.rs b/compiler/rustc_codegen_cranelift/build_system/main.rs index 99e6146657f3..3ff9751a3ef2 100644 --- a/compiler/rustc_codegen_cranelift/build_system/main.rs +++ b/compiler/rustc_codegen_cranelift/build_system/main.rs @@ -156,10 +156,8 @@ fn main() { let cargo = rustc_info::get_cargo_path(); let rustc = rustc_info::get_rustc_path(); let rustdoc = rustc_info::get_rustdoc_path(); - let triple = std::env::var("HOST_TRIPLE") - .ok() - .or_else(|| config::get_value("host")) - .unwrap_or_else(|| rustc_info::get_host_triple(&rustc)); + let triple = + std::env::var("HOST_TRIPLE").unwrap_or_else(|_| rustc_info::get_host_triple(&rustc)); Compiler { cargo, rustc, @@ -170,10 +168,8 @@ fn main() { runner: vec![], } }; - let target_triple = std::env::var("TARGET_TRIPLE") - .ok() - .or_else(|| config::get_value("target")) - .unwrap_or_else(|| bootstrap_host_compiler.triple.clone()); + let target_triple = + std::env::var("TARGET_TRIPLE").unwrap_or_else(|_| bootstrap_host_compiler.triple.clone()); let dirs = path::Dirs { source_dir: current_dir.clone(), @@ -247,7 +243,7 @@ fn main() { ); } Command::Bench => { - build_sysroot::build_sysroot( + let compiler = build_sysroot::build_sysroot( &dirs, sysroot_kind, &cg_clif_dylib, @@ -255,7 +251,7 @@ fn main() { rustup_toolchain_name.as_deref(), target_triple, ); - bench::benchmark(&dirs, &bootstrap_host_compiler); + bench::benchmark(&dirs, &compiler); } } } diff --git a/compiler/rustc_codegen_cranelift/build_system/path.rs b/compiler/rustc_codegen_cranelift/build_system/path.rs index 20a81156b71d..d6a6558b2be2 100644 --- a/compiler/rustc_codegen_cranelift/build_system/path.rs +++ b/compiler/rustc_codegen_cranelift/build_system/path.rs @@ -11,20 +11,11 @@ pub(crate) struct Dirs { #[doc(hidden)] #[derive(Debug, Copy, Clone)] -pub(crate) enum PathBase { +enum PathBase { Source, Build, } -impl PathBase { - fn to_path(self, dirs: &Dirs) -> PathBuf { - match self { - PathBase::Source => dirs.source_dir.clone(), - PathBase::Build => dirs.build_dir.clone(), - } - } -} - #[derive(Debug, Copy, Clone)] pub(crate) struct RelPath { base: PathBase, @@ -41,6 +32,9 @@ impl RelPath { } pub(crate) fn to_path(&self, dirs: &Dirs) -> PathBuf { - self.base.to_path(dirs).join(self.suffix) + match self.base { + PathBase::Source => dirs.source_dir.join(self.suffix), + PathBase::Build => dirs.build_dir.join(self.suffix), + } } } diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index b63597f60fc6..9808ad624e11 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -1,15 +1,5 @@ # This file allows configuring the build system. -# Which triple to produce a compiler toolchain for. -# -# Defaults to the default triple of rustc on the host system. -#host = x86_64-unknown-linux-gnu - -# Which triple to build libraries (core/alloc/std/test/proc_macro) for. -# -# Defaults to `host`. -#target = x86_64-unknown-linux-gnu - # Disables cleaning of the sysroot dir. This will cause old compiled artifacts to be re-used when # the sysroot source hasn't changed. This is useful when the codegen backend hasn't been modified. # This option can be changed while the build system is already running for as long as sysroot diff --git a/compiler/rustc_codegen_cranelift/patches/bcryptprimitives.rs b/compiler/rustc_codegen_cranelift/patches/bcryptprimitives.rs deleted file mode 100644 index 4d186485aac1..000000000000 --- a/compiler/rustc_codegen_cranelift/patches/bcryptprimitives.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Shim for bcryptprimitives.dll. The Wine version shipped with Ubuntu 22.04 -// doesn't support it yet. Authored by @ChrisDenton - -#![crate_type = "cdylib"] -#![allow(nonstandard_style)] - -#[no_mangle] -pub unsafe extern "system" fn ProcessPrng(mut pbData: *mut u8, mut cbData: usize) -> i32 { - while cbData > 0 { - let size = core::cmp::min(cbData, u32::MAX as usize); - RtlGenRandom(pbData, size as u32); - cbData -= size; - pbData = pbData.add(size); - } - 1 -} - -#[link(name = "advapi32")] -extern "system" { - #[link_name = "SystemFunction036"] - pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: u32) -> u8; -} diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index 8d935df4d1f2..4b97f2105798 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-12-06" +channel = "nightly-2025-01-05" components = ["rust-src", "rustc-dev", "llvm-tools"] profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index e291ec204649..442d61c6ade1 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -123,12 +123,17 @@ rm tests/ui/mir/mir_raw_fat_ptr.rs # same rm tests/ui/consts/issue-33537.rs # same rm tests/ui/consts/const-mut-refs-crate.rs # same rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift +rm tests/ui/invalid-compile-flags/crate-type-flag.rs # warning about proc-macros and panic=abort # doesn't work due to the way the rustc test suite is invoked. # should work when using ./x.py test the way it is intended # ============================================================ rm -r tests/run-make/remap-path-prefix-dwarf # requires llvm-dwarfdump +rm -r tests/run-make/strip # same rm -r tests/run-make/compiler-builtins # Expects lib/rustlib/src/rust to contains the standard library source +rm -r tests/run-make/missing-unstable-trait-bound # This disables support for unstable features, but running cg_clif needs some unstable features +rm -r tests/run-make/const-trait-stable-toolchain # same +rm -r tests/run-make/incr-add-rust-src-component # genuine bugs # ============ @@ -196,5 +201,5 @@ index e7ae773ffa1d3..04bc2d7787da7 100644 EOF echo "[TEST] rustc test suite" -COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 --test-args=--nocapture tests/{codegen-units,run-make,ui,incremental} +COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,ui,incremental} popd diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 4fc30b69123d..fe578e44770f 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -333,10 +333,9 @@ fn make_module(sess: &Session, name: String) -> UnwindModule { let mut builder = ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap(); - // Unlike cg_llvm, cg_clif defaults to disabling -Zfunction-sections. For cg_llvm binary size - // is important, while cg_clif cares more about compilation times. Enabling -Zfunction-sections - // can easily double the amount of time necessary to perform linking. - builder.per_function_section(sess.opts.unstable_opts.function_sections.unwrap_or(false)); + builder.per_function_section( + sess.opts.unstable_opts.function_sections.unwrap_or(sess.target.function_sections), + ); UnwindModule::new(ObjectModule::new(builder), true) } diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index eaab3362c7e8..9ca930e14da5 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -287,7 +287,7 @@ fn dep_symbol_lookup_fn( let mut dylib_paths = Vec::new(); - let data = &crate_info.dependency_formats[&rustc_session::config::CrateType::Executable].1; + let data = &crate_info.dependency_formats[&rustc_session::config::CrateType::Executable]; // `used_crates` is in reverse postorder in terms of dependencies. Reverse the order here to // get a postorder which ensures that all dependencies of a dylib are loaded before the dylib // itself. This helps the dynamic linker to find dylibs not in the regular dynamic library diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 33726056cc1c..6ff75f75d3b2 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -136,7 +136,7 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>( fx.bcx.ins().jump(destination_block, &[]); } None => { - fx.bcx.ins().trap(TrapCode::user(0 /* unreachable */).unwrap()); + fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap()); } } } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index e0ebe30752af..6d71b8e8abab 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -1136,7 +1136,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( _ => { fx.tcx.dcx().span_err(span, format!("Unknown SIMD intrinsic {}", intrinsic)); // Prevent verifier error - fx.bcx.ins().trap(TrapCode::user(0 /* unreachable */).unwrap()); + fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap()); return; } } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index c38ef82e5b80..dc5d80e7a345 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -27,6 +27,8 @@ extern crate rustc_metadata; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; +#[macro_use] +extern crate tracing; // This prevents duplicating functions and statics that are already part of the host rustc process. #[allow(unused_extern_crates)] @@ -208,6 +210,7 @@ impl CodegenBackend for CraneliftCodegenBackend { need_metadata_module: bool, ) -> Box { tcx.dcx().abort_if_errors(); + info!("codegen crate {}", tcx.crate_name(LOCAL_CRATE)); let config = self.config.clone().unwrap_or_else(|| { BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args) .unwrap_or_else(|err| tcx.sess.dcx().fatal(err)) diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index ed92f9c52412..f7173d4d2ffc 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -660,9 +660,7 @@ pub unsafe fn optimize_thin_module( { let _timer = cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name()); - if !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) { - return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule)); - } + unsafe { llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) }; save_temp_bitcode(cgcx, &module, "thin-lto-after-rename"); } diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index 56849cc86109..c896246866b9 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -28,6 +28,7 @@ pub(crate) struct UnstableCTargetFeature<'a> { #[diag(codegen_gcc_forbidden_ctarget_feature)] pub(crate) struct ForbiddenCTargetFeature<'a> { pub feature: &'a str, + pub enabled: &'a str, pub reason: &'a str, } diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 058a874501b2..1994a2a3c537 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -1,9 +1,11 @@ +use std::iter::FromIterator; + #[cfg(feature = "master")] use gccjit::Context; use rustc_codegen_ssa::codegen_attrs::check_tied_features; use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable; -use rustc_data_structures::fx::FxHashMap; -use rustc_middle::bug; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::unord::UnordSet; use rustc_session::Session; use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; use smallvec::{SmallVec, smallvec}; @@ -37,82 +39,137 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec return None, - Some(c @ ('+' | '-')) => c, - Some(_) => { - if diagnostics { - sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); - } - return None; - } - }; - // Get the backend feature name, if any. - // This excludes rustc-specific features, that do not get passed down to GCC. - let feature = backend_feature_name(s)?; - // Warn against use of GCC specific feature names on the CLI. + // Ensure that all ABI-required features are enabled, and the ABI-forbidden ones + // are disabled. + let abi_feature_constraints = sess.target.abi_required_features(); + let abi_incompatible_set = + FxHashSet::from_iter(abi_feature_constraints.incompatible.iter().copied()); + + // Compute implied features + let mut all_rust_features = vec![]; + for feature in sess.opts.cg.target_feature.split(',') { + if let Some(feature) = feature.strip_prefix('+') { + all_rust_features.extend( + UnordSet::from(sess.target.implied_target_features(std::iter::once(feature))) + .to_sorted_stable_ord() + .iter() + .map(|&&s| (true, s)), + ) + } else if let Some(feature) = feature.strip_prefix('-') { + // FIXME: Why do we not remove implied features on "-" here? + // We do the equivalent above in `target_features_cfg`. + // See . + all_rust_features.push((false, feature)); + } else if !feature.is_empty() { if diagnostics { - let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); - match feature_state { - None => { - let rust_feature = - known_features.iter().find_map(|&(rust_feature, _, _)| { - let gcc_features = to_gcc_features(sess, rust_feature); - if gcc_features.contains(&feature) - && !gcc_features.contains(&rust_feature) - { - Some(rust_feature) - } else { - None - } - }); - let unknown_feature = if let Some(rust_feature) = rust_feature { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::Some { rust_feature }, - } - } else { - UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } - }; - sess.dcx().emit_warn(unknown_feature); - } - Some((_, stability, _)) => { - if let Err(reason) = - stability.toggle_allowed(&sess.target, enable_disable == '+') + sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature }); + } + } + } + // Remove features that are meant for rustc, not codegen. + all_rust_features.retain(|(_, feature)| { + // Retain if it is not a rustc feature + !RUSTC_SPECIFIC_FEATURES.contains(feature) + }); + + // Check feature validity. + if diagnostics { + for &(enable, feature) in &all_rust_features { + let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); + match feature_state { + None => { + let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| { + let gcc_features = to_gcc_features(sess, rust_feature); + if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature) { - sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason }); - } else if stability.requires_nightly().is_some() { - // An unstable feature. Warn about using it. (It makes little sense - // to hard-error here since we just warn about fully unknown - // features above). - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + Some(rust_feature) + } else { + None } + }); + let unknown_feature = if let Some(rust_feature) = rust_feature { + UnknownCTargetFeature { + feature, + rust_feature: PossibleFeature::Some { rust_feature }, + } + } else { + UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } + }; + sess.dcx().emit_warn(unknown_feature); + } + Some((_, stability, _)) => { + if let Err(reason) = stability.toggle_allowed() { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: if enable { "enabled" } else { "disabled" }, + reason, + }); + } else if stability.requires_nightly().is_some() { + // An unstable feature. Warn about using it. (It makes little sense + // to hard-error here since we just warn about fully unknown + // features above). + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); } } - - // FIXME(nagisa): figure out how to not allocate a full hashset here. - featsmap.insert(feature, enable_disable == '+'); } - // ... otherwise though we run through `to_gcc_features` when + // Ensure that the features we enable/disable are compatible with the ABI. + if enable { + if abi_incompatible_set.contains(feature) { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: "enabled", + reason: "this feature is incompatible with the target ABI", + }); + } + } else { + // FIXME: we have to request implied features here since + // negative features do not handle implied features above. + for &required in abi_feature_constraints.required.iter() { + let implied = sess.target.implied_target_features(std::iter::once(required)); + if implied.contains(feature) { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: "disabled", + reason: "this feature is required by the target ABI", + }); + } + } + } + + // FIXME(nagisa): figure out how to not allocate a full hashset here. + featsmap.insert(feature, enable); + } + } + + // To be sure the ABI-relevant features are all in the right state, we explicitly + // (un)set them here. This means if the target spec sets those features wrong, + // we will silently correct them rather than silently producing wrong code. + // (The target sanity check tries to catch this, but we can't know which features are + // enabled in GCC by default so we can't be fully sure about that check.) + // We add these at the beginning of the list so that `-Ctarget-features` can + // still override it... that's unsound, but more compatible with past behavior. + all_rust_features.splice( + 0..0, + abi_feature_constraints + .required + .iter() + .map(|&f| (true, f)) + .chain(abi_feature_constraints.incompatible.iter().map(|&f| (false, f))), + ); + + // Translate this into GCC features. + let feats = all_rust_features + .iter() + .filter_map(|&(enable, feature)| { + let enable_disable = if enable { '+' } else { '-' }; + // We run through `to_gcc_features` when // passing requests down to GCC. This means that all in-language // features also work on the command line instead of having two // different names when the GCC name and the Rust name differ. @@ -146,26 +203,12 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec Option<&str> { - // features must start with a `+` or `-`. - let feature = s.strip_prefix(&['+', '-'][..]).unwrap_or_else(|| { - bug!("target feature `{}` must begin with a `+` or `-`", s); - }); - // Rustc-specific feature requests like `+crt-static` or `-crt-static` - // are not passed down to GCC. - if RUSTC_SPECIFIC_FEATURES.contains(&feature) { - return None; - } - Some(feature) -} - // To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> { let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch }; match (arch, s) { + // FIXME: seems like x87 does not exist? + ("x86", "x87") => smallvec![], ("x86", "sse4.2") => smallvec!["sse4.2", "crc32"], ("x86", "pclmulqdq") => smallvec!["pclmul"], ("x86", "rdrand") => smallvec!["rdrnd"], diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 689986d642d9..c44d1a5e5c22 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -9,6 +9,7 @@ test = false [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" +gimli = "0.30" itertools = "0.12" libc = "0.2" measureme = "11" diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index 3982c37528df..9585848cbf03 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -10,7 +10,7 @@ codegen_llvm_dynamic_linking_with_lto = codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture codegen_llvm_forbidden_ctarget_feature = - target feature `{$feature}` cannot be toggled with `-Ctarget-feature`: {$reason} + target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason} .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! codegen_llvm_forbidden_ctarget_feature_issue = for more information, see issue #116344 @@ -24,8 +24,6 @@ codegen_llvm_invalid_minimum_alignment_not_power_of_two = codegen_llvm_invalid_minimum_alignment_too_large = invalid minimum global alignment: {$align} is too large -codegen_llvm_invalid_target_feature_prefix = target feature `{$feature}` must begin with a `+` or `-`" - codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}" codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err} diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 4adf99e91d08..08b774f8d6ec 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -737,11 +737,7 @@ pub(crate) unsafe fn optimize_thin_module( { let _timer = cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name()); - if unsafe { - !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) - } { - return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule)); - } + unsafe { llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) }; save_temp_bitcode(cgcx, &module, "thin-lto-after-rename"); } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs new file mode 100644 index 000000000000..408429152223 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs @@ -0,0 +1,37 @@ +//! Definitions of various DWARF-related constants. + +use libc::c_uint; + +/// Helper macro to let us redeclare gimli's constants as our own constants +/// with a different type, with less risk of copy-paste errors. +macro_rules! declare_constant { + ( + $name:ident : $type:ty + ) => { + #[allow(non_upper_case_globals)] + pub(crate) const $name: $type = ::gimli::constants::$name.0 as $type; + + // Assert that as-cast probably hasn't changed the value. + const _: () = assert!($name as i128 == ::gimli::constants::$name.0 as i128); + }; +} + +declare_constant!(DW_TAG_const_type: c_uint); + +// DWARF languages. +declare_constant!(DW_LANG_Rust: c_uint); + +// DWARF attribute type encodings. +declare_constant!(DW_ATE_boolean: c_uint); +declare_constant!(DW_ATE_float: c_uint); +declare_constant!(DW_ATE_signed: c_uint); +declare_constant!(DW_ATE_unsigned: c_uint); +declare_constant!(DW_ATE_UTF: c_uint); + +// DWARF expression operators. +declare_constant!(DW_OP_deref: u64); +declare_constant!(DW_OP_plus_uconst: u64); +/// Defined by LLVM in `llvm/include/llvm/BinaryFormat/Dwarf.h`. +/// Double-checked by a static assertion in `RustWrapper.cpp`. +#[allow(non_upper_case_globals)] +pub(crate) const DW_OP_LLVM_fragment: u64 = 0x1000; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 12411b21ba32..88e43e1c678a 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -22,6 +22,7 @@ use rustc_target::spec::DebuginfoKind; use smallvec::smallvec; use tracing::{debug, instrument}; +pub(crate) use self::type_map::TypeMap; use self::type_map::{DINodeCreationResult, Stub, UniqueTypeId}; use super::CodegenUnitDebugContext; use super::namespace::mangled_name_of_instance; @@ -30,6 +31,7 @@ use super::utils::{ DIB, create_DIArray, debug_context, get_namespace_for_item, is_node_local_to_unit, }; use crate::common::{AsCCharPtr, CodegenCx}; +use crate::debuginfo::dwarf_const; use crate::debuginfo::metadata::type_map::build_type_with_children; use crate::debuginfo::utils::{WidePtrKind, wide_pointer_kind}; use crate::llvm::debuginfo::{ @@ -59,23 +61,6 @@ impl fmt::Debug for llvm::Metadata { } } -// From DWARF 5. -// See http://www.dwarfstd.org/ShowIssue.php?issue=140129.1. -const DW_LANG_RUST: c_uint = 0x1c; -#[allow(non_upper_case_globals)] -const DW_ATE_boolean: c_uint = 0x02; -#[allow(non_upper_case_globals)] -const DW_ATE_float: c_uint = 0x04; -#[allow(non_upper_case_globals)] -const DW_ATE_signed: c_uint = 0x05; -#[allow(non_upper_case_globals)] -const DW_ATE_unsigned: c_uint = 0x07; -#[allow(non_upper_case_globals)] -const DW_ATE_UTF: c_uint = 0x10; - -#[allow(non_upper_case_globals)] -const DW_TAG_const_type: c_uint = 0x26; - pub(super) const UNKNOWN_LINE_NUMBER: c_uint = 0; pub(super) const UNKNOWN_COLUMN_NUMBER: c_uint = 0; @@ -90,8 +75,6 @@ type SmallVec = smallvec::SmallVec<[T; 16]>; mod enums; mod type_map; -pub(crate) use type_map::TypeMap; - /// Returns from the enclosing function if the type debuginfo node with the given /// unique ID can be found in the type map. macro_rules! return_if_di_node_created_in_meantime { @@ -522,7 +505,7 @@ fn recursion_marker_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> &'ll D name.as_c_char_ptr(), name.len(), cx.tcx.data_layout.pointer_size.bits(), - DW_ATE_unsigned, + dwarf_const::DW_ATE_unsigned, ) } }) @@ -781,6 +764,8 @@ fn build_basic_type_di_node<'ll, 'tcx>( // .natvis visualizers (and perhaps other existing native debuggers?) let cpp_like_debuginfo = cpp_like_debuginfo(cx.tcx); + use dwarf_const::{DW_ATE_UTF, DW_ATE_boolean, DW_ATE_float, DW_ATE_signed, DW_ATE_unsigned}; + let (name, encoding) = match t.kind() { ty::Never => ("!", DW_ATE_unsigned), ty::Tuple(elements) if elements.is_empty() => { @@ -961,7 +946,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit( debug_context.builder, - DW_LANG_RUST, + dwarf_const::DW_LANG_Rust, compile_unit_file, producer.as_c_char_ptr(), producer.len(), diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index dd83714614da..a72e205c9b24 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -12,12 +12,13 @@ use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty}; use smallvec::smallvec; use crate::common::{AsCCharPtr, CodegenCx}; +use crate::debuginfo::dwarf_const::DW_TAG_const_type; use crate::debuginfo::metadata::enums::DiscrResult; use crate::debuginfo::metadata::type_map::{self, Stub, UniqueTypeId}; use crate::debuginfo::metadata::{ - DINodeCreationResult, DW_TAG_const_type, NO_GENERICS, NO_SCOPE_METADATA, SmallVec, - UNKNOWN_LINE_NUMBER, build_field_di_node, file_metadata, file_metadata_from_def_id, - size_and_align_of, type_di_node, unknown_file_metadata, visibility_di_flags, + DINodeCreationResult, NO_GENERICS, NO_SCOPE_METADATA, SmallVec, UNKNOWN_LINE_NUMBER, + build_field_di_node, file_metadata, file_metadata_from_def_id, size_and_align_of, type_di_node, + unknown_file_metadata, visibility_di_flags, }; use crate::debuginfo::utils::DIB; use crate::llvm::debuginfo::{DIFile, DIFlags, DIType}; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index fae698bea2a6..755f4816acf9 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -39,6 +39,7 @@ use crate::llvm::debuginfo::{ use crate::value::Value; mod create_scope_map; +mod dwarf_const; mod gdb; pub(crate) mod metadata; mod namespace; @@ -47,6 +48,10 @@ mod utils; use self::create_scope_map::compute_mir_scopes; pub(crate) use self::metadata::build_global_var_di_node; +// FIXME(Zalathar): These `DW_TAG_*` constants are fake values that were +// removed from LLVM in 2015, and are only used by our own `RustWrapper.cpp` +// to decide which C++ API to call. Instead, we should just have two separate +// FFI functions and choose the correct one on the Rust side. #[allow(non_upper_case_globals)] const DW_TAG_auto_variable: c_uint = 0x100; #[allow(non_upper_case_globals)] @@ -152,29 +157,26 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { indirect_offsets: &[Size], fragment: Option>, ) { + use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst}; + // Convert the direct and indirect offsets and fragment byte range to address ops. - // FIXME(eddyb) use `const`s instead of getting the values via FFI, - // the values should match the ones in the DWARF standard anyway. - let op_deref = || unsafe { llvm::LLVMRustDIBuilderCreateOpDeref() }; - let op_plus_uconst = || unsafe { llvm::LLVMRustDIBuilderCreateOpPlusUconst() }; - let op_llvm_fragment = || unsafe { llvm::LLVMRustDIBuilderCreateOpLLVMFragment() }; let mut addr_ops = SmallVec::<[u64; 8]>::new(); if direct_offset.bytes() > 0 { - addr_ops.push(op_plus_uconst()); + addr_ops.push(DW_OP_plus_uconst); addr_ops.push(direct_offset.bytes() as u64); } for &offset in indirect_offsets { - addr_ops.push(op_deref()); + addr_ops.push(DW_OP_deref); if offset.bytes() > 0 { - addr_ops.push(op_plus_uconst()); + addr_ops.push(DW_OP_plus_uconst); addr_ops.push(offset.bytes() as u64); } } if let Some(fragment) = fragment { // `DW_OP_LLVM_fragment` takes as arguments the fragment's // offset and size, both of them in bits. - addr_ops.push(op_llvm_fragment()); + addr_ops.push(DW_OP_LLVM_fragment); addr_ops.push(fragment.start.bits() as u64); addr_ops.push((fragment.end - fragment.start).bits() as u64); } diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index f340b06e876c..f4c9491f7584 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -37,6 +37,7 @@ pub(crate) struct UnstableCTargetFeature<'a> { #[note(codegen_llvm_forbidden_ctarget_feature_issue)] pub(crate) struct ForbiddenCTargetFeature<'a> { pub feature: &'a str, + pub enabled: &'a str, pub reason: &'a str, } @@ -213,12 +214,6 @@ pub(crate) struct MismatchedDataLayout<'a> { pub llvm_layout: &'a str, } -#[derive(Diagnostic)] -#[diag(codegen_llvm_invalid_target_feature_prefix)] -pub(crate) struct InvalidTargetFeaturePrefix<'a> { - pub feature: &'a str, -} - #[derive(Diagnostic)] #[diag(codegen_llvm_fixed_x18_invalid_arch)] pub(crate) struct FixedX18InvalidArch<'a> { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 21be66cb29b6..cb4a8c9a5f21 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2177,9 +2177,6 @@ unsafe extern "C" { Location: &'a DILocation, BD: c_uint, ) -> Option<&'a DILocation>; - pub fn LLVMRustDIBuilderCreateOpDeref() -> u64; - pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> u64; - pub fn LLVMRustDIBuilderCreateOpLLVMFragment() -> u64; pub fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString); pub fn LLVMRustWriteValueToString(value_ref: &Value, s: &RustString); @@ -2377,7 +2374,7 @@ unsafe extern "C" { Data: &ThinLTOData, Module: &Module, Target: &TargetMachine, - ) -> bool; + ); pub fn LLVMRustPrepareThinLTOResolveWeak(Data: &ThinLTOData, Module: &Module) -> bool; pub fn LLVMRustPrepareThinLTOInternalize(Data: &ThinLTOData, Module: &Module) -> bool; pub fn LLVMRustPrepareThinLTOImport( diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 628c0b1c29c4..c3d7c217861f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -21,8 +21,8 @@ use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATU use crate::back::write::create_informational_target_machine; use crate::errors::{ - FixedX18InvalidArch, ForbiddenCTargetFeature, InvalidTargetFeaturePrefix, PossibleFeature, - UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature, + FixedX18InvalidArch, ForbiddenCTargetFeature, PossibleFeature, UnknownCTargetFeature, + UnknownCTargetFeaturePrefix, UnstableCTargetFeature, }; use crate::llvm; @@ -109,7 +109,10 @@ unsafe fn configure_llvm(sess: &Session) { add("-wasm-enable-eh", false); } - if sess.target.os == "emscripten" && sess.panic_strategy() == PanicStrategy::Unwind { + if sess.target.os == "emscripten" + && !sess.opts.unstable_opts.emscripten_wasm_eh + && sess.panic_strategy() == PanicStrategy::Unwind + { add("-enable-emscripten-cxx-exceptions", false); } @@ -348,7 +351,16 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec { if enabled { // Also add all transitively implied features. - features.extend(sess.target.implied_target_features(std::iter::once(feature))); + + // We don't care about the order in `features` since the only thing we use it for is the + // `features.contains` below. + #[allow(rustc::potential_query_instability)] + features.extend( + sess.target + .implied_target_features(std::iter::once(feature.as_str())) + .iter() + .map(|s| Symbol::intern(s)), + ); } else { // Remove transitively reverse-implied features. @@ -356,7 +368,11 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec // `features.contains` below. #[allow(rustc::potential_query_instability)] features.retain(|f| { - if sess.target.implied_target_features(std::iter::once(*f)).contains(&feature) { + if sess + .target + .implied_target_features(std::iter::once(f.as_str())) + .contains(&feature.as_str()) + { // If `f` if implies `feature`, then `!feature` implies `!f`, so we have to // remove `f`. (This is the standard logical contraposition principle.) false @@ -638,7 +654,7 @@ pub(crate) fn global_llvm_features( sess.target .features .split(',') - .filter(|v| !v.is_empty() && backend_feature_name(sess, v).is_some()) + .filter(|v| !v.is_empty()) // Drop +v8plus feature introduced in LLVM 20. .filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0)) .map(String::from), @@ -651,89 +667,136 @@ pub(crate) fn global_llvm_features( // -Ctarget-features if !only_base_features { let known_features = sess.target.rust_target_features(); + // Will only be filled when `diagnostics` is set! let mut featsmap = FxHashMap::default(); - // insert implied features + // Ensure that all ABI-required features are enabled, and the ABI-forbidden ones + // are disabled. + let abi_feature_constraints = sess.target.abi_required_features(); + let abi_incompatible_set = + FxHashSet::from_iter(abi_feature_constraints.incompatible.iter().copied()); + + // Compute implied features let mut all_rust_features = vec![]; for feature in sess.opts.cg.target_feature.split(',') { - match feature.strip_prefix('+') { - Some(feature) => all_rust_features.extend( - UnordSet::from( - sess.target - .implied_target_features(std::iter::once(Symbol::intern(feature))), - ) - .to_sorted_stable_ord() - .iter() - .map(|s| format!("+{}", s.as_str())), - ), - _ => all_rust_features.push(feature.to_string()), + if let Some(feature) = feature.strip_prefix('+') { + all_rust_features.extend( + UnordSet::from(sess.target.implied_target_features(std::iter::once(feature))) + .to_sorted_stable_ord() + .iter() + .map(|&&s| (true, s)), + ) + } else if let Some(feature) = feature.strip_prefix('-') { + // FIXME: Why do we not remove implied features on "-" here? + // We do the equivalent above in `target_features_cfg`. + // See . + all_rust_features.push((false, feature)); + } else if !feature.is_empty() { + if diagnostics { + sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature }); + } + } + } + // Remove features that are meant for rustc, not LLVM. + all_rust_features.retain(|(_, feature)| { + // Retain if it is not a rustc feature + !RUSTC_SPECIFIC_FEATURES.contains(feature) + }); + + // Check feature validity. + if diagnostics { + for &(enable, feature) in &all_rust_features { + let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); + match feature_state { + None => { + let rust_feature = + known_features.iter().find_map(|&(rust_feature, _, _)| { + let llvm_features = to_llvm_features(sess, rust_feature)?; + if llvm_features.contains(feature) + && !llvm_features.contains(rust_feature) + { + Some(rust_feature) + } else { + None + } + }); + let unknown_feature = if let Some(rust_feature) = rust_feature { + UnknownCTargetFeature { + feature, + rust_feature: PossibleFeature::Some { rust_feature }, + } + } else { + UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } + }; + sess.dcx().emit_warn(unknown_feature); + } + Some((_, stability, _)) => { + if let Err(reason) = stability.toggle_allowed() { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: if enable { "enabled" } else { "disabled" }, + reason, + }); + } else if stability.requires_nightly().is_some() { + // An unstable feature. Warn about using it. It makes little sense + // to hard-error here since we just warn about fully unknown + // features above. + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + } + } + } + + // Ensure that the features we enable/disable are compatible with the ABI. + if enable { + if abi_incompatible_set.contains(feature) { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: "enabled", + reason: "this feature is incompatible with the target ABI", + }); + } + } else { + // FIXME: we have to request implied features here since + // negative features do not handle implied features above. + for &required in abi_feature_constraints.required.iter() { + let implied = + sess.target.implied_target_features(std::iter::once(required)); + if implied.contains(feature) { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: "disabled", + reason: "this feature is required by the target ABI", + }); + } + } + } + + // FIXME(nagisa): figure out how to not allocate a full hashset here. + featsmap.insert(feature, enable); } } + // To be sure the ABI-relevant features are all in the right state, we explicitly + // (un)set them here. This means if the target spec sets those features wrong, + // we will silently correct them rather than silently producing wrong code. + // (The target sanity check tries to catch this, but we can't know which features are + // enabled in LLVM by default so we can't be fully sure about that check.) + // We add these at the beginning of the list so that `-Ctarget-features` can + // still override it... that's unsound, but more compatible with past behavior. + all_rust_features.splice( + 0..0, + abi_feature_constraints + .required + .iter() + .map(|&f| (true, f)) + .chain(abi_feature_constraints.incompatible.iter().map(|&f| (false, f))), + ); + + // Translate this into LLVM features. let feats = all_rust_features .iter() - .filter_map(|s| { - let enable_disable = match s.chars().next() { - None => return None, - Some(c @ ('+' | '-')) => c, - Some(_) => { - if diagnostics { - sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); - } - return None; - } - }; - - // Get the backend feature name, if any. - // This excludes rustc-specific features, which do not get passed to LLVM. - let feature = backend_feature_name(sess, s)?; - // Warn against use of LLVM specific feature names and unstable features on the CLI. - if diagnostics { - let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); - match feature_state { - None => { - let rust_feature = - known_features.iter().find_map(|&(rust_feature, _, _)| { - let llvm_features = to_llvm_features(sess, rust_feature)?; - if llvm_features.contains(feature) - && !llvm_features.contains(rust_feature) - { - Some(rust_feature) - } else { - None - } - }); - let unknown_feature = if let Some(rust_feature) = rust_feature { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::Some { rust_feature }, - } - } else { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::None, - } - }; - sess.dcx().emit_warn(unknown_feature); - } - Some((_, stability, _)) => { - if let Err(reason) = - stability.toggle_allowed(&sess.target, enable_disable == '+') - { - sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason }); - } else if stability.requires_nightly().is_some() { - // An unstable feature. Warn about using it. It makes little sense - // to hard-error here since we just warn about fully unknown - // features above. - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); - } - } - } - - // FIXME(nagisa): figure out how to not allocate a full hashset here. - featsmap.insert(feature, enable_disable == '+'); - } - + .filter_map(|&(enable, feature)| { + let enable_disable = if enable { '+' } else { '-' }; // We run through `to_llvm_features` when // passing requests down to LLVM. This means that all in-language // features also work on the command line instead of having two @@ -746,9 +809,9 @@ pub(crate) fn global_llvm_features( enable_disable, llvm_feature.llvm_feature_name )) .chain(llvm_feature.dependency.into_iter().filter_map( - move |feat| match (enable_disable, feat) { - ('-' | '+', TargetFeatureFoldStrength::Both(f)) - | ('+', TargetFeatureFoldStrength::EnableOnly(f)) => { + move |feat| match (enable, feat) { + (_, TargetFeatureFoldStrength::Both(f)) + | (true, TargetFeatureFoldStrength::EnableOnly(f)) => { Some(format!("{enable_disable}{f}")) } _ => None, @@ -780,22 +843,6 @@ pub(crate) fn global_llvm_features( features } -/// Returns a feature name for the given `+feature` or `-feature` string. -/// -/// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].) -fn backend_feature_name<'a>(sess: &Session, s: &'a str) -> Option<&'a str> { - // features must start with a `+` or `-`. - let feature = s - .strip_prefix(&['+', '-'][..]) - .unwrap_or_else(|| sess.dcx().emit_fatal(InvalidTargetFeaturePrefix { feature: s })); - // Rustc-specific feature requests like `+crt-static` or `-crt-static` - // are not passed down to LLVM. - if s.is_empty() || RUSTC_SPECIFIC_FEATURES.contains(&feature) { - return None; - } - Some(feature) -} - pub(crate) fn tune_cpu(sess: &Session) -> Option<&str> { let name = sess.opts.unstable_opts.tune_cpu.as_ref()?; Some(handle_native(name)) diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 56188714b44f..484f467068a1 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -67,7 +67,7 @@ codegen_ssa_failed_to_write = failed to write {$path}: {$error} codegen_ssa_field_associated_value_expected = associated value expected for `{$name}` codegen_ssa_forbidden_target_feature_attr = - target feature `{$feature}` cannot be toggled with `#[target_feature]`: {$reason} + target feature `{$feature}` cannot be enabled with `#[target_feature]`: {$reason} codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 2587d6dfdc4b..e4b3ad198018 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2451,10 +2451,12 @@ fn add_order_independent_options( } if sess.target.os == "emscripten" { - cmd.cc_arg("-s").cc_arg(if sess.panic_strategy() == PanicStrategy::Abort { - "DISABLE_EXCEPTION_CATCHING=1" + cmd.cc_arg(if sess.panic_strategy() == PanicStrategy::Abort { + "-sDISABLE_EXCEPTION_CATCHING=1" + } else if sess.opts.unstable_opts.emscripten_wasm_eh { + "-fwasm-exceptions" } else { - "DISABLE_EXCEPTION_CATCHING=0" + "-sDISABLE_EXCEPTION_CATCHING=0" }); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 77e1fed720df..544578b29f10 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -388,7 +388,8 @@ pub(crate) fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // exceptions. This means that the VM does the unwinding for // us pub fn wants_wasm_eh(sess: &Session) -> bool { - sess.target.is_like_wasm && sess.target.os != "emscripten" + sess.target.is_like_wasm + && (sess.target.os != "emscripten" || sess.opts.unstable_opts.emscripten_wasm_eh) } /// Returns `true` if this session's target will use SEH-based unwinding. diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 7e80d014ea28..d8b9bdb55da6 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -19,7 +19,7 @@ use crate::errors; pub(crate) fn from_target_feature_attr( tcx: TyCtxt<'_>, attr: &hir::Attribute, - rust_target_features: &UnordMap, + rust_target_features: &UnordMap, target_features: &mut Vec, ) { let Some(list) = attr.meta_item_list() else { return }; @@ -32,7 +32,7 @@ pub(crate) fn from_target_feature_attr( .emit(); }; let rust_features = tcx.features(); - let mut added_target_features = Vec::new(); + let abi_feature_constraints = tcx.sess.target.abi_required_features(); for item in list { // Only `enable = ...` is accepted in the meta-item list. if !item.has_name(sym::enable) { @@ -47,7 +47,7 @@ pub(crate) fn from_target_feature_attr( }; // We allow comma separation to enable multiple features. - added_target_features.extend(value.as_str().split(',').filter_map(|feature| { + for feature in value.as_str().split(',') { let Some(stability) = rust_target_features.get(feature) else { let msg = format!("the feature named `{feature}` is not valid for this target"); let mut err = tcx.dcx().struct_span_err(item.span(), msg); @@ -59,12 +59,12 @@ pub(crate) fn from_target_feature_attr( } } err.emit(); - return None; + continue; }; // Only allow target features whose feature gates have been enabled // and which are permitted to be toggled. - if let Err(reason) = stability.toggle_allowed(/*enable*/ true) { + if let Err(reason) = stability.toggle_allowed() { tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { span: item.span(), feature, @@ -80,31 +80,25 @@ pub(crate) fn from_target_feature_attr( format!("the target feature `{feature}` is currently unstable"), ) .emit(); + } else { + // Add this and the implied features. + let feature_sym = Symbol::intern(feature); + for &name in tcx.implied_target_features(feature_sym) { + // But ensure the ABI does not forbid enabling this. + // Here we do assume that LLVM doesn't add even more implied features + // we don't know about, at least no features that would have ABI effects! + if abi_feature_constraints.incompatible.contains(&name.as_str()) { + tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { + span: item.span(), + feature: name.as_str(), + reason: "this feature is incompatible with the target ABI", + }); + } + target_features.push(TargetFeature { name, implied: name != feature_sym }) + } } - Some(Symbol::intern(feature)) - })); + } } - - // Add explicit features - target_features.extend( - added_target_features.iter().copied().map(|name| TargetFeature { name, implied: false }), - ); - - // Add implied features - let mut implied_target_features = UnordSet::new(); - for feature in added_target_features.iter() { - implied_target_features.extend(tcx.implied_target_features(*feature).clone()); - } - for feature in added_target_features.iter() { - implied_target_features.remove(feature); - } - target_features.extend( - implied_target_features - .into_sorted_stable_ord() - .iter() - .copied() - .map(|name| TargetFeature { name, implied: true }), - ) } /// Computes the set of target features used in a function for the purposes of @@ -147,25 +141,28 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { rust_target_features: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); - let target = &tcx.sess.target; if tcx.sess.opts.actually_rustdoc { // rustdoc needs to be able to document functions that use all the features, so // whitelist them all rustc_target::target_features::all_rust_features() - .map(|(a, b)| (a.to_string(), b.compute_toggleability(target))) + .map(|(a, b)| (a.to_string(), b)) .collect() } else { tcx.sess .target .rust_target_features() .iter() - .map(|(a, b, _)| (a.to_string(), b.compute_toggleability(target))) + .map(|(a, b, _)| (a.to_string(), *b)) .collect() } }, - implied_target_features: |tcx, feature| { + implied_target_features: |tcx, feature: Symbol| { + let feature = feature.as_str(); UnordSet::from(tcx.sess.target.implied_target_features(std::iter::once(feature))) .into_sorted_stable_ord() + .into_iter() + .map(|s| Symbol::intern(s)) + .collect() }, asm_target_features, ..*providers diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index b861ffb6110d..5d905cff1f21 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -704,8 +704,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &str> { let len = mplace.len(self)?; let bytes = self.read_bytes_ptr_strip_provenance(mplace.ptr(), Size::from_bytes(len))?; - let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; - interp_ok(str) + let s = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; + interp_ok(s) } /// Read from a local of the current frame. Convenience method for [`InterpCx::local_at_frame_to_op`]. diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 0d9740716193..c97922ac132b 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -1017,9 +1017,9 @@ where /// This is allocated in immutable global memory and deduplicated. pub fn allocate_str_dedup( &mut self, - str: &str, + s: &str, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - let bytes = str.as_bytes(); + let bytes = s.as_bytes(); let ptr = self.allocate_bytes_dedup(bytes)?; // Create length metadata for the string. diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 90f382e72268..0413e5e86348 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -52,8 +52,8 @@ use rustc_metadata::locator; use rustc_middle::ty::TyCtxt; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::config::{ - CG_OPTIONS, ErrorOutputType, Input, OutFileName, OutputType, UnstableOptions, Z_OPTIONS, - nightly_options, + CG_OPTIONS, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, UnstableOptions, + Z_OPTIONS, nightly_options, }; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; @@ -1124,14 +1124,6 @@ pub fn describe_flag_categories(early_dcx: &EarlyDiagCtxt, matches: &Matches) -> return true; } - if cg_flags.iter().any(|x| *x == "no-stack-check") { - early_dcx.early_warn("the `-Cno-stack-check` flag is deprecated and does nothing"); - } - - if cg_flags.iter().any(|x| x.starts_with("inline-threshold")) { - early_dcx.early_warn("the `-Cinline-threshold` flag is deprecated and does nothing (consider using `-Cllvm-args=--inline-threshold=...`)"); - } - if cg_flags.iter().any(|x| *x == "passes=list") { let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); @@ -1156,18 +1148,16 @@ fn describe_codegen_flags() { print_flag_list("-C", config::CG_OPTIONS); } -fn print_flag_list( - cmdline_opt: &str, - flag_list: &[(&'static str, T, &'static str, &'static str)], -) { - let max_len = flag_list.iter().map(|&(name, _, _, _)| name.chars().count()).max().unwrap_or(0); +fn print_flag_list(cmdline_opt: &str, flag_list: &[OptionDesc]) { + let max_len = + flag_list.iter().map(|opt_desc| opt_desc.name().chars().count()).max().unwrap_or(0); - for &(name, _, _, desc) in flag_list { + for opt_desc in flag_list { safe_println!( " {} {:>width$}=val -- {}", cmdline_opt, - name.replace('_', "-"), - desc, + opt_desc.name().replace('_', "-"), + opt_desc.desc(), width = max_len ); } @@ -1221,8 +1211,8 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option = match e { getopts::Fail::UnrecognizedOption(ref opt) => CG_OPTIONS .iter() - .map(|&(name, ..)| ('C', name)) - .chain(Z_OPTIONS.iter().map(|&(name, ..)| ('Z', name))) + .map(|opt_desc| ('C', opt_desc.name())) + .chain(Z_OPTIONS.iter().map(|opt_desc| ('Z', opt_desc.name()))) .find(|&(_, name)| *opt == name.replace('_', "-")) .map(|(flag, _)| format!("{e}. Did you mean `-{flag} {opt}`?")), getopts::Fail::ArgumentMissing(ref opt) => { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 00bb4d655790..5421517046d8 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -37,6 +37,7 @@ const GATED_CFGS: &[GatedCfg] = &[ (sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi), // this is consistent with naming of the compiler flag it's for (sym::fmt_debug, sym::fmt_debug, Features::fmt_debug), + (sym::emscripten_wasm_eh, sym::cfg_emscripten_wasm_eh, Features::cfg_emscripten_wasm_eh), ]; /// Find a gated cfg determined by the `pred`icate which is given the cfg's name. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index d40823d2ed62..8cc4c18c02ab 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -202,6 +202,8 @@ declare_features! ( (internal, allow_internal_unstable, "1.0.0", None), /// Allows using anonymous lifetimes in argument-position impl-trait. (unstable, anonymous_lifetime_in_impl_trait, "1.63.0", None), + /// Allows access to the emscripten_wasm_eh config, used by panic_unwind and unwind + (internal, cfg_emscripten_wasm_eh, "CURRENT_RUSTC_VERSION", None), /// Allows identifying the `compiler_builtins` crate. (internal, compiler_builtins, "1.13.0", None), /// Allows writing custom MIR diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 6b4b716d9a80..2e4b6748731a 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1387,7 +1387,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true, - Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it), + Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it), Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)), Slice(before, slice, after) => { @@ -1414,7 +1414,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {} - Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it), + Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it), Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)), Slice(before, slice, after) => { @@ -1566,6 +1566,9 @@ pub enum PatKind<'hir> { /// A literal. Lit(&'hir Expr<'hir>), + /// A guard pattern (e.g., `x if guard(x)`). + Guard(&'hir Pat<'hir>, &'hir Expr<'hir>), + /// A range pattern (e.g., `1..=2` or `1..2`). Range(Option<&'hir Expr<'hir>>, Option<&'hir Expr<'hir>>, RangeEnd), diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 85e555d903b9..f0480afd139f 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -696,6 +696,10 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: visit_opt!(visitor, visit_pat, slice_pattern); walk_list!(visitor, visit_pat, postpatterns); } + PatKind::Guard(subpat, condition) => { + try_visit!(visitor.visit_pat(subpat)); + try_visit!(visitor.visit_expr(condition)); + } } V::Result::output() } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 5548a6a6ef79..8c6059d49a84 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1845,13 +1845,18 @@ pub(super) fn check_coroutine_obligations( debug!(?typeck_results.coroutine_stalled_predicates); + let mode = if tcx.next_trait_solver_globally() { + TypingMode::post_borrowck_analysis(tcx, def_id) + } else { + TypingMode::analysis_in_body(tcx, def_id) + }; + let infcx = tcx .infer_ctxt() // typeck writeback gives us predicates with their regions erased. // As borrowck already has checked lifetimes, we do not need to do it again. .ignoring_regions() - // FIXME(#132279): This should eventually use the already defined hidden types. - .build(TypingMode::analysis_in_body(tcx, def_id)); + .build(mode); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); for (predicate, cause) in &typeck_results.coroutine_stalled_predicates { @@ -1864,12 +1869,14 @@ pub(super) fn check_coroutine_obligations( return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } - // Check that any hidden types found when checking these stalled coroutine obligations - // are valid. - for (key, ty) in infcx.take_opaque_types() { - let hidden_type = infcx.resolve_vars_if_possible(ty.hidden_type); - let key = infcx.resolve_vars_if_possible(key); - sanity_check_found_hidden_type(tcx, key, hidden_type)?; + if !tcx.next_trait_solver_globally() { + // Check that any hidden types found when checking these stalled coroutine obligations + // are valid. + for (key, ty) in infcx.take_opaque_types() { + let hidden_type = infcx.resolve_vars_if_possible(ty.hidden_type); + let key = infcx.resolve_vars_if_possible(key); + sanity_check_found_hidden_type(tcx, key, hidden_type)?; + } } Ok(()) diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index ca6729a5bbdf..ffc0657eebf1 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -654,6 +654,7 @@ fn resolve_local<'tcx>( /// | ( ..., P&, ... ) /// | ... "|" P& "|" ... /// | box P& + /// | P& if ... /// ``` fn is_binding_pat(pat: &hir::Pat<'_>) -> bool { // Note that the code below looks for *explicit* refs only, that is, it won't @@ -694,7 +695,9 @@ fn resolve_local<'tcx>( | PatKind::TupleStruct(_, subpats, _) | PatKind::Tuple(subpats, _) => subpats.iter().any(|p| is_binding_pat(p)), - PatKind::Box(subpat) | PatKind::Deref(subpat) => is_binding_pat(subpat), + PatKind::Box(subpat) | PatKind::Deref(subpat) | PatKind::Guard(subpat, _) => { + is_binding_pat(subpat) + } PatKind::Ref(_, _) | PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..) diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index bb122009593b..42034736ad67 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -128,21 +128,7 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained( for param in &impl_generics.own_params { match param.kind { ty::GenericParamDefKind::Lifetime => { - let param_lt = cgp::Parameter::from(param.to_early_bound_region_data()); - if lifetimes_in_associated_types.contains(¶m_lt) // (*) - && !input_parameters.contains(¶m_lt) - { - let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter { - span: tcx.def_span(param.def_id), - param_name: param.name, - param_def_kind: tcx.def_descr(param.def_id), - const_param_note: false, - const_param_note2: false, - }); - diag.code(E0207); - res = Err(diag.emit()); - } - // (*) This is a horrible concession to reality. I think it'd be + // This is a horrible concession to reality. I think it'd be // better to just ban unconstrained lifetimes outright, but in // practice people do non-hygienic macros like: // @@ -160,6 +146,20 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained( // permit those, so long as the lifetimes aren't used in // associated types. I believe this is sound, because lifetimes // used elsewhere are not projected back out. + let param_lt = cgp::Parameter::from(param.to_early_bound_region_data()); + if lifetimes_in_associated_types.contains(¶m_lt) + && !input_parameters.contains(¶m_lt) + { + let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter { + span: tcx.def_span(param.def_id), + param_name: param.name, + param_def_kind: tcx.def_descr(param.def_id), + const_param_note: false, + const_param_note2: false, + }); + diag.code(E0207); + res = Err(diag.emit()); + } } ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => { // Enforced in `enforce_impl_non_lifetime_params_are_constrained`. diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 42c98ab434ef..16bb0dd328cd 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1999,6 +1999,12 @@ impl<'a> State<'a> { self.commasep(Inconsistent, after, |s, p| s.print_pat(p)); self.word("]"); } + PatKind::Guard(inner, cond) => { + self.print_pat(inner); + self.space(); + self.word_space("if"); + self.print_expr(cond); + } PatKind::Err(_) => { self.popen(); self.word("/*ERROR*/"); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index fb73985e3060..8c8a8d6d9228 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -456,6 +456,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Does not constitute a read. hir::PatKind::Wild => false, + // Might not constitute a read, since the condition might be false. + hir::PatKind::Guard(_, _) => true, + // This is unnecessarily restrictive when the pattern that doesn't // constitute a read is unreachable. // diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index ecbae6ac72fa..ad764e5563b9 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -615,6 +615,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx | PatKind::Box(_) | PatKind::Deref(_) | PatKind::Ref(..) + | PatKind::Guard(..) | PatKind::Wild | PatKind::Err(_) => { // If the PatKind is Or, Box, or Ref, the decision is made later @@ -1737,7 +1738,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } } - PatKind::Binding(.., Some(subpat)) => { + PatKind::Binding(.., Some(subpat)) | PatKind::Guard(subpat, _) => { self.cat_pattern(place_with_id, subpat, op)?; } diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 789530d35dd8..f4929aae5990 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -40,13 +40,25 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// FIXME: Move this check out of typeck, since it'll easily cycle when revealing opaques, + /// and we shouldn't need to check anything here if the typeck results are tainted. pub(crate) fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) { let tcx = self.tcx; let dl = &tcx.data_layout; let span = tcx.hir().span(hir_id); let normalize = |ty| { let ty = self.resolve_vars_if_possible(ty); - self.tcx.normalize_erasing_regions(self.typing_env(self.param_env), ty) + if let Ok(ty) = + self.tcx.try_normalize_erasing_regions(self.typing_env(self.param_env), ty) + { + ty + } else { + Ty::new_error_with_message( + tcx, + span, + "tried to normalize non-wf type in check_transmute", + ) + } }; let from = normalize(from); let to = normalize(to); diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 5a0a855147de..a406ec9a8fbb 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -147,8 +147,23 @@ fn typeck_with_fallback<'tcx>( check_abi(tcx, span, fn_sig.abi()); // Compute the function signature from point of view of inside the fn. - let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); - let fn_sig = fcx.normalize(body.value.span, fn_sig); + let mut fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); + + // Normalize the input and output types one at a time, using a different + // `WellFormedLoc` for each. We cannot call `normalize_associated_types` + // on the entire `FnSig`, since this would use the same `WellFormedLoc` + // for each type, preventing the HIR wf check from generating + // a nice error message. + let arg_span = + |idx| decl.inputs.get(idx).map_or(decl.output.span(), |arg: &hir::Ty<'_>| arg.span); + + fn_sig.inputs_and_output = tcx.mk_type_list_from_iter( + fn_sig + .inputs_and_output + .iter() + .enumerate() + .map(|(idx, ty)| fcx.normalize(arg_span(idx), ty)), + ); check_fn(&mut fcx, fn_sig, None, decl, def_id, body, tcx.features().unsized_fn_params()); } else { diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 0b008fd10b50..b4f1dcfb9cc8 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -14,7 +14,7 @@ use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, MultiSpan, StashKey, pluralize, struct_span_code_err}; -use rustc_hir::def::DefKind; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::lang_items::LangItem; @@ -690,6 +690,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } + // Check if we wrote `Self::Assoc(1)` as if it were a tuple ctor. + if let SelfSource::QPath(ty) = source + && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind + && let Res::SelfTyAlias { alias_to: impl_def_id, .. } = path.res + && let DefKind::Impl { .. } = self.tcx.def_kind(impl_def_id) + && let Some(candidate) = tcx.associated_items(impl_def_id).find_by_name_and_kind( + self.tcx, + item_name, + ty::AssocKind::Type, + impl_def_id, + ) + && let Some(adt_def) = tcx.type_of(candidate.def_id).skip_binder().ty_adt_def() + && adt_def.is_struct() + && adt_def.non_enum_variant().ctor_kind() == Some(CtorKind::Fn) + { + let def_path = tcx.def_path_str(adt_def.did()); + err.span_suggestion( + ty.span.to(item_name.span), + format!("to construct a value of type `{}`, use the explicit path", def_path), + def_path, + Applicability::MachineApplicable, + ); + } + err }; if tcx.sess.source_map().is_multiline(sugg_span) { diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 98b28240f4cb..4870e6193c3f 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -284,6 +284,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Struct(ref qpath, fields, has_rest_pat) => { self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info) } + PatKind::Guard(pat, cond) => { + self.check_pat(pat, expected, pat_info); + self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {}); + expected + } PatKind::Or(pats) => { for pat in pats { self.check_pat(pat, expected, pat_info); @@ -422,7 +427,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // An OR-pattern just propagates to each individual alternative. // This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`. // In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`. - | PatKind::Or(_) => AdjustMode::Pass, + | PatKind::Or(_) + // Like or-patterns, guard patterns just propogate to their subpatterns. + | PatKind::Guard(..) => AdjustMode::Pass, } } @@ -901,6 +908,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Or(..) + | PatKind::Guard(..) | PatKind::Tuple(..) | PatKind::Slice(..) => "binding", diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 9ad690399140..53d7c84ac3f8 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -582,6 +582,7 @@ fn test_codegen_options_tracking_hash() { untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe"))); untracked!(extra_filename, String::from("extra-filename")); untracked!(incremental, Some(String::from("abc"))); + untracked!(inline_threshold, Some(0xf007ba11)); // `link_arg` is omitted because it just forwards to `link_args`. untracked!(link_args, vec![String::from("abc"), String::from("def")]); untracked!(link_self_contained, LinkSelfContained::on()); @@ -613,7 +614,6 @@ fn test_codegen_options_tracking_hash() { tracked!(embed_bitcode, false); tracked!(force_frame_pointers, FramePointer::Always); tracked!(force_unwind_tables, Some(true)); - tracked!(inline_threshold, Some(0xf007ba11)); tracked!(instrument_coverage, InstrumentCoverage::Yes); tracked!(link_dead_code, Some(true)); tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); @@ -782,6 +782,7 @@ fn test_unstable_options_tracking_hash() { tracked!(dwarf_version, Some(5)); tracked!(embed_source, true); tracked!(emit_thin_lto, false); + tracked!(emscripten_wasm_eh, true); tracked!(export_executable_symbols, true); tracked!(fewer_names, Some(true)); tracked!(fixed_x18, true); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 2feed5e80dc5..e74fb9d92e9e 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1244,8 +1244,8 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { declare_lint! { /// The `unreachable_pub` lint triggers for `pub` items not reachable from other crates - that - /// means neither directly accessible, nor reexported, nor leaked through things like return - /// types. + /// means neither directly accessible, nor reexported (with `pub use`), nor leaked through + /// things like return types (which the [`unnameable_types`] lint can detect if desired). /// /// ### Example /// @@ -1272,8 +1272,10 @@ declare_lint! { /// intent that the item is only visible within its own crate. /// /// This lint is "allow" by default because it will trigger for a large - /// amount existing Rust code, and has some false-positives. Eventually it + /// amount of existing Rust code, and has some false-positives. Eventually it /// is desired for this to become warn-by-default. + /// + /// [`unnameable_types`]: #unnameable-types pub UNREACHABLE_PUB, Allow, "`pub` items not reachable from crate root" diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 70dce78b5724..e09049f322fa 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -234,10 +234,10 @@ declare_lint! { declare_lint_pass!(NonSnakeCase => [NON_SNAKE_CASE]); impl NonSnakeCase { - fn to_snake_case(mut str: &str) -> String { + fn to_snake_case(mut name: &str) -> String { let mut words = vec![]; // Preserve leading underscores - str = str.trim_start_matches(|c: char| { + name = name.trim_start_matches(|c: char| { if c == '_' { words.push(String::new()); true @@ -245,7 +245,7 @@ impl NonSnakeCase { false } }); - for s in str.split('_') { + for s in name.split('_') { let mut last_upper = false; let mut buf = String::new(); if s.is_empty() { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 2f23ab27492c..8399f4c12f4f 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4362,7 +4362,7 @@ declare_lint! { /// ### Explanation /// /// It is often expected that if you can obtain an object of type `T`, then - /// you can name the type `T` as well, this lint attempts to enforce this rule. + /// you can name the type `T` as well; this lint attempts to enforce this rule. /// The recommended action is to either reexport the type properly to make it nameable, /// or document that users are not supposed to be able to name it for one reason or another. /// diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index de14c6d18836..6447a9362b3a 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1389,20 +1389,14 @@ static bool clearDSOLocalOnDeclarations(Module &Mod, TargetMachine &TM) { return ClearDSOLocalOnDeclarations; } -extern "C" bool LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, +extern "C" void LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M, LLVMTargetMachineRef TM) { Module &Mod = *unwrap(M); TargetMachine &Target = *unwrap(TM); bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target); - bool error = renameModuleForThinLTO(Mod, Data->Index, ClearDSOLocal); - - if (error) { - LLVMRustSetLastError("renameModuleForThinLTO failed"); - return false; - } - return true; + renameModuleForThinLTO(Mod, Data->Index, ClearDSOLocal); } extern "C" bool diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index ca62483b0aa2..dd72ea2497f7 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -54,6 +54,10 @@ using namespace llvm; using namespace llvm::sys; using namespace llvm::object; +// This opcode is an LLVM detail that could hypothetically change (?), so +// verify that the hard-coded value in `dwarf_const.rs` still agrees with LLVM. +static_assert(dwarf::DW_OP_LLVM_fragment == 0x1000); + // LLVMAtomicOrdering is already an enum - don't create another // one. static AtomicOrdering fromRust(LLVMAtomicOrdering Ordering) { @@ -1397,18 +1401,6 @@ LLVMRustDILocationCloneWithBaseDiscriminator(LLVMMetadataRef Location, return wrap(NewLoc.has_value() ? NewLoc.value() : nullptr); } -extern "C" uint64_t LLVMRustDIBuilderCreateOpDeref() { - return dwarf::DW_OP_deref; -} - -extern "C" uint64_t LLVMRustDIBuilderCreateOpPlusUconst() { - return dwarf::DW_OP_plus_uconst; -} - -extern "C" uint64_t LLVMRustDIBuilderCreateOpLLVMFragment() { - return dwarf::DW_OP_LLVM_fragment; -} - extern "C" void LLVMRustWriteTypeToString(LLVMTypeRef Ty, RustStringRef Str) { auto OS = RawRustStringOstream(Str); unwrap(Ty)->print(OS); diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index a3890fc937e7..d0ef82f4a6ce 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -130,11 +130,11 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> { let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer); match cfg.backtrace { - Ok(str) => { + Ok(backtrace_target) => { let fmt_layer = tracing_subscriber::fmt::layer() .with_writer(io::stderr) .without_time() - .event_format(BacktraceFormatter { backtrace_target: str }); + .event_format(BacktraceFormatter { backtrace_target }); let subscriber = subscriber.with(fmt_layer); tracing::subscriber::set_global_default(subscriber).unwrap(); } diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index 2552c0a0cfc7..37200f62eb5a 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -156,14 +156,14 @@ impl Entries { Entries { map: HashMap::with_capacity(capacity) } } - fn insert(&mut self, span: Span, str: &str, errors: &mut Errors) -> u32 { - if let Some(prev) = self.map.get(str) { - errors.error(span, format!("Symbol `{str}` is duplicated")); + fn insert(&mut self, span: Span, s: &str, errors: &mut Errors) -> u32 { + if let Some(prev) = self.map.get(s) { + errors.error(span, format!("Symbol `{s}` is duplicated")); errors.error(prev.span_of_name, "location of previous definition".to_string()); prev.idx } else { let idx = self.len(); - self.map.insert(str.to_string(), Preinterned { idx, span_of_name: span }); + self.map.insert(s.to_string(), Preinterned { idx, span_of_name: span }); idx } } @@ -192,14 +192,14 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let mut entries = Entries::with_capacity(input.keywords.len() + input.symbols.len() + 10); let mut prev_key: Option<(Span, String)> = None; - let mut check_order = |span: Span, str: &str, errors: &mut Errors| { + let mut check_order = |span: Span, s: &str, errors: &mut Errors| { if let Some((prev_span, ref prev_str)) = prev_key { - if str < prev_str { - errors.error(span, format!("Symbol `{str}` must precede `{prev_str}`")); + if s < prev_str { + errors.error(span, format!("Symbol `{s}` must precede `{prev_str}`")); errors.error(prev_span, format!("location of previous symbol `{prev_str}`")); } } - prev_key = Some((span, str.to_string())); + prev_key = Some((span, s.to_string())); }; // Generate the listed keywords. diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index e64500f812a1..2c34df6ea61a 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -derive-where = "1.2.7" either = "1.5.0" field-offset = "0.3.5" gsgdt = "0.1.2" diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 241767fe249b..16d868300db3 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -28,6 +28,7 @@ pub struct CodegenFnAttrs { pub link_ordinal: Option, /// The `#[target_feature(enable = "...")]` attribute and the enabled /// features (only enabled features are supported right now). + /// Implied target features have already been applied. pub target_features: Vec, /// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found. pub linkage: Option, diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 429be9bc725b..db5da941f1e7 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -3,7 +3,6 @@ use std::cell::Cell; use std::fmt::{self, Debug}; -use derive_where::derive_where; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; @@ -225,29 +224,22 @@ rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16); /// See also `rustc_const_eval::borrow_check::constraints`. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)] -#[derive_where(PartialOrd, Ord)] pub enum ConstraintCategory<'tcx> { Return(ReturnConstraint), Yield, UseAsConst, UseAsStatic, - TypeAnnotation, + TypeAnnotation(AnnotationSource), Cast { /// Whether this cast is a coercion that was automatically inserted by the compiler. is_implicit_coercion: bool, /// Whether this is an unsizing coercion and if yes, this contains the target type. /// Region variables are erased to ReErased. - #[derive_where(skip)] unsize_to: Option>, }, - /// A constraint that came from checking the body of a closure. - /// - /// We try to get the category that the closure used when reporting this. - ClosureBounds, - /// Contains the function type if available. - CallArgument(#[derive_where(skip)] Option>), + CallArgument(Option>), CopyBound, SizedBound, Assignment, @@ -276,13 +268,22 @@ pub enum ConstraintCategory<'tcx> { IllegalUniverse, } -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)] pub enum ReturnConstraint { Normal, ClosureUpvar(FieldIdx), } +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)] +pub enum AnnotationSource { + Ascription, + Declaration, + OpaqueCast, + GenericArg, +} + /// The subject of a `ClosureOutlivesRequirement` -- that is, the thing /// that must outlive some region. #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index da3fa9e324a4..470a247d794e 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -455,6 +455,8 @@ impl BorrowKind { } } + /// Returns whether borrows represented by this kind are allowed to be split into separate + /// Reservation and Activation phases. pub fn allows_two_phase_borrow(&self) -> bool { match *self { BorrowKind::Shared diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index c960f03cf061..95995b956cda 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -2357,7 +2357,7 @@ rustc_queries! { } /// Returns the Rust target features for the current target. These are not always the same as LLVM target features! - query rust_target_features(_: CrateNum) -> &'tcx UnordMap { + query rust_target_features(_: CrateNum) -> &'tcx UnordMap { arena_cache eval_always desc { "looking up Rust target features" } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 99211c1f9244..db2bb8a7248a 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -125,6 +125,15 @@ impl<'tcx> ObligationCause<'tcx> { self } + pub fn derived_host_cause( + mut self, + parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, + variant: impl FnOnce(DerivedHostCause<'tcx>) -> ObligationCauseCode<'tcx>, + ) -> ObligationCause<'tcx> { + self.code = variant(DerivedHostCause { parent_host_pred, parent_code: self.code }).into(); + self + } + pub fn to_constraint_category(&self) -> ConstraintCategory<'tcx> { match self.code() { ObligationCauseCode::MatchImpl(cause, _) => cause.to_constraint_category(), @@ -278,6 +287,14 @@ pub enum ObligationCauseCode<'tcx> { /// Derived obligation for WF goals. WellFormedDerived(DerivedCause<'tcx>), + /// Derived obligation (i.e. `where` clause) on an user-provided impl + /// or a trait alias. + ImplDerivedHost(Box>), + + /// Derived obligation (i.e. `where` clause) on an user-provided impl + /// or a trait alias. + BuiltinDerivedHost(DerivedHostCause<'tcx>), + /// Derived obligation refined to point at a specific argument in /// a call or method expression. FunctionArg { @@ -437,36 +454,38 @@ pub enum WellFormedLoc { }, } -#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] -#[derive(TypeVisitable, TypeFoldable)] -pub struct ImplDerivedCause<'tcx> { - pub derived: DerivedCause<'tcx>, - /// The `DefId` of the `impl` that gave rise to the `derived` obligation. - /// If the `derived` obligation arose from a trait alias, which conceptually has a synthetic impl, - /// then this will be the `DefId` of that trait alias. Care should therefore be taken to handle - /// that exceptional case where appropriate. - pub impl_or_alias_def_id: DefId, - /// The index of the derived predicate in the parent impl's predicates. - pub impl_def_predicate_index: Option, - pub span: Span, -} - impl<'tcx> ObligationCauseCode<'tcx> { /// Returns the base obligation, ignoring derived obligations. pub fn peel_derives(&self) -> &Self { let mut base_cause = self; - while let Some((parent_code, _)) = base_cause.parent() { + while let Some(parent_code) = base_cause.parent() { base_cause = parent_code; } base_cause } + pub fn parent(&self) -> Option<&Self> { + match self { + ObligationCauseCode::FunctionArg { parent_code, .. } => Some(parent_code), + ObligationCauseCode::BuiltinDerived(derived) + | ObligationCauseCode::WellFormedDerived(derived) + | ObligationCauseCode::ImplDerived(box ImplDerivedCause { derived, .. }) => { + Some(&derived.parent_code) + } + ObligationCauseCode::BuiltinDerivedHost(derived) + | ObligationCauseCode::ImplDerivedHost(box ImplDerivedHostCause { derived, .. }) => { + Some(&derived.parent_code) + } + _ => None, + } + } + /// Returns the base obligation and the base trait predicate, if any, ignoring /// derived obligations. pub fn peel_derives_with_predicate(&self) -> (&Self, Option>) { let mut base_cause = self; let mut base_trait_pred = None; - while let Some((parent_code, parent_pred)) = base_cause.parent() { + while let Some((parent_code, parent_pred)) = base_cause.parent_with_predicate() { base_cause = parent_code; if let Some(parent_pred) = parent_pred { base_trait_pred = Some(parent_pred); @@ -476,7 +495,7 @@ impl<'tcx> ObligationCauseCode<'tcx> { (base_cause, base_trait_pred) } - pub fn parent(&self) -> Option<(&Self, Option>)> { + pub fn parent_with_predicate(&self) -> Option<(&Self, Option>)> { match self { ObligationCauseCode::FunctionArg { parent_code, .. } => Some((parent_code, None)), ObligationCauseCode::BuiltinDerived(derived) @@ -573,6 +592,42 @@ pub struct DerivedCause<'tcx> { pub parent_code: InternedObligationCauseCode<'tcx>, } +#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +#[derive(TypeVisitable, TypeFoldable)] +pub struct ImplDerivedCause<'tcx> { + pub derived: DerivedCause<'tcx>, + /// The `DefId` of the `impl` that gave rise to the `derived` obligation. + /// If the `derived` obligation arose from a trait alias, which conceptually has a synthetic impl, + /// then this will be the `DefId` of that trait alias. Care should therefore be taken to handle + /// that exceptional case where appropriate. + pub impl_or_alias_def_id: DefId, + /// The index of the derived predicate in the parent impl's predicates. + pub impl_def_predicate_index: Option, + pub span: Span, +} + +#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +#[derive(TypeVisitable, TypeFoldable)] +pub struct DerivedHostCause<'tcx> { + /// The trait predicate of the parent obligation that led to the + /// current obligation. Note that only trait obligations lead to + /// derived obligations, so we just store the trait predicate here + /// directly. + pub parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, + + /// The parent trait had this cause. + pub parent_code: InternedObligationCauseCode<'tcx>, +} + +#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +#[derive(TypeVisitable, TypeFoldable)] +pub struct ImplDerivedHostCause<'tcx> { + pub derived: DerivedHostCause<'tcx>, + /// The `DefId` of the `impl` that gave rise to the `derived` obligation. + pub impl_def_id: DefId, + pub span: Span, +} + #[derive(Clone, Debug, PartialEq, Eq, TypeVisitable)] pub enum SelectionError<'tcx> { /// The trait is not implemented. diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 3ecaa3e22d39..32d6455e8255 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -634,6 +634,28 @@ impl<'tcx> UpcastFrom, PolyProjectionPredicate<'tcx>> for Clause<'t } } +impl<'tcx> UpcastFrom, ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>> + for Predicate<'tcx> +{ + fn upcast_from( + from: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, + tcx: TyCtxt<'tcx>, + ) -> Self { + from.map_bound(ty::ClauseKind::HostEffect).upcast(tcx) + } +} + +impl<'tcx> UpcastFrom, ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>> + for Clause<'tcx> +{ + fn upcast_from( + from: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, + tcx: TyCtxt<'tcx>, + ) -> Self { + from.map_bound(ty::ClauseKind::HostEffect).upcast(tcx) + } +} + impl<'tcx> UpcastFrom, NormalizesTo<'tcx>> for Predicate<'tcx> { fn upcast_from(from: NormalizesTo<'tcx>, tcx: TyCtxt<'tcx>) -> Self { PredicateKind::NormalizesTo(from).upcast(tcx) diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index b0150bc11920..72f353f06ff5 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -45,10 +45,25 @@ pub trait Printer<'tcx>: Sized { &mut self, impl_def_id: DefId, args: &'tcx [GenericArg<'tcx>], - self_ty: Ty<'tcx>, - trait_ref: Option>, ) -> Result<(), PrintError> { - self.default_print_impl_path(impl_def_id, args, self_ty, trait_ref) + let tcx = self.tcx(); + let self_ty = tcx.type_of(impl_def_id); + let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); + let (self_ty, impl_trait_ref) = if tcx.generics_of(impl_def_id).count() <= args.len() { + ( + self_ty.instantiate(tcx, args), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate(tcx, args)), + ) + } else { + // We are probably printing a nested item inside of an impl. + // Use the identity substitutions for the impl. + ( + self_ty.instantiate_identity(), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate_identity()), + ) + }; + + self.default_print_impl_path(impl_def_id, self_ty, impl_trait_ref) } fn print_region(&mut self, region: ty::Region<'tcx>) -> Result<(), PrintError>; @@ -107,23 +122,7 @@ pub trait Printer<'tcx>: Sized { self.path_crate(def_id.krate) } - DefPathData::Impl => { - let generics = self.tcx().generics_of(def_id); - let self_ty = self.tcx().type_of(def_id); - let impl_trait_ref = self.tcx().impl_trait_ref(def_id); - let (self_ty, impl_trait_ref) = if args.len() >= generics.count() { - ( - self_ty.instantiate(self.tcx(), args), - impl_trait_ref.map(|i| i.instantiate(self.tcx(), args)), - ) - } else { - ( - self_ty.instantiate_identity(), - impl_trait_ref.map(|i| i.instantiate_identity()), - ) - }; - self.print_impl_path(def_id, args, self_ty, impl_trait_ref) - } + DefPathData::Impl => self.print_impl_path(def_id, args), _ => { let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id }; @@ -201,7 +200,6 @@ pub trait Printer<'tcx>: Sized { fn default_print_impl_path( &mut self, impl_def_id: DefId, - _args: &'tcx [GenericArg<'tcx>], self_ty: Ty<'tcx>, impl_trait_ref: Option>, ) -> Result<(), PrintError> { diff --git a/compiler/rustc_middle/src/util/find_self_call.rs b/compiler/rustc_middle/src/util/find_self_call.rs index ec6051d0a771..0fdd35207386 100644 --- a/compiler/rustc_middle/src/util/find_self_call.rs +++ b/compiler/rustc_middle/src/util/find_self_call.rs @@ -17,26 +17,29 @@ pub fn find_self_call<'tcx>( debug!("find_self_call(local={:?}): terminator={:?}", local, body[block].terminator); if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) = &body[block].terminator + && let Operand::Constant(box ConstOperand { const_, .. }) = func + && let ty::FnDef(def_id, fn_args) = *const_.ty().kind() + && let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) = + tcx.opt_associated_item(def_id) + && let [Spanned { node: Operand::Move(self_place) | Operand::Copy(self_place), .. }, ..] = + **args { - debug!("find_self_call: func={:?}", func); - if let Operand::Constant(box ConstOperand { const_, .. }) = func { - if let ty::FnDef(def_id, fn_args) = *const_.ty().kind() { - if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) = - tcx.opt_associated_item(def_id) - { - debug!("find_self_call: args={:?}", fn_args); - if let [ - Spanned { - node: Operand::Move(self_place) | Operand::Copy(self_place), .. - }, - .., - ] = **args - { - if self_place.as_local() == Some(local) { - return Some((def_id, fn_args)); - } - } - } + if self_place.as_local() == Some(local) { + return Some((def_id, fn_args)); + } + + // Handle the case where `self_place` gets reborrowed. + // This happens when the receiver is `&T`. + for stmt in &body[block].statements { + if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind + && let Some(reborrow_local) = place.as_local() + && self_place.as_local() == Some(reborrow_local) + && let Rvalue::Ref(_, _, deref_place) = rvalue + && let PlaceRef { local: deref_local, projection: [ProjectionElem::Deref] } = + deref_place.as_ref() + && deref_local == local + { + return Some((def_id, fn_args)); } } } diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 35c98037827a..20441530a479 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -1131,15 +1131,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Schedule emission of a backwards incompatible drop lint hint. /// Applicable only to temporary values for now. + #[instrument(level = "debug", skip(self))] pub(crate) fn schedule_backwards_incompatible_drop( &mut self, span: Span, region_scope: region::Scope, local: Local, ) { - if !self.local_decls[local].ty.has_significant_drop(self.tcx, self.typing_env()) { - return; - } + // Note that we are *not* gating BIDs here on whether they have significant destructor. + // We need to know all of them so that we can capture potential borrow-checking errors. for scope in self.scopes.scopes.iter_mut().rev() { // Since we are inserting linting MIR statement, we have to invalidate the caches scope.invalidate_cache(); diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index a13b00e19216..663108672006 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1086,14 +1086,7 @@ fn find_fallback_pattern_typo<'tcx>( let vis = cx.tcx.visibility(item.owner_id); if vis.is_accessible_from(parent, cx.tcx) { accessible.push(item_name); - let path = if item_name == name { - // We know that the const wasn't in scope because it has the exact - // same name, so we suggest the full path. - with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id)) - } else { - // The const is likely just typoed, and nothing else. - cx.tcx.def_path_str(item.owner_id) - }; + let path = with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id)); accessible_path.push(path); } else if name == item_name { // The const exists somewhere in this crate, but it can't be imported diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index bdf243c87b6f..54510faf2e18 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -435,6 +435,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir::PatKind::Or(pats) => PatKind::Or { pats: self.lower_patterns(pats) }, + // FIXME(guard_patterns): implement guard pattern lowering + hir::PatKind::Guard(pat, _) => self.lower_pattern(pat).kind, + hir::PatKind::Err(guar) => PatKind::Error(guar), }; diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs index b909dfa13205..d6ecadbfe29c 100644 --- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs +++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs @@ -29,13 +29,8 @@ fn build_ptr_tys<'tcx>( pub(super) fn build_projection<'tcx>( unique_ty: Ty<'tcx>, nonnull_ty: Ty<'tcx>, - ptr_ty: Ty<'tcx>, -) -> [PlaceElem<'tcx>; 3] { - [ - PlaceElem::Field(FieldIdx::ZERO, unique_ty), - PlaceElem::Field(FieldIdx::ZERO, nonnull_ty), - PlaceElem::Field(FieldIdx::ZERO, ptr_ty), - ] +) -> [PlaceElem<'tcx>; 2] { + [PlaceElem::Field(FieldIdx::ZERO, unique_ty), PlaceElem::Field(FieldIdx::ZERO, nonnull_ty)] } struct ElaborateBoxDerefVisitor<'a, 'tcx> { @@ -75,10 +70,14 @@ impl<'a, 'tcx> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'a, 'tcx> { self.patch.add_assign( location, Place::from(ptr_local), - Rvalue::Use(Operand::Copy( - Place::from(place.local) - .project_deeper(&build_projection(unique_ty, nonnull_ty, ptr_ty), tcx), - )), + Rvalue::Cast( + CastKind::Transmute, + Operand::Copy( + Place::from(place.local) + .project_deeper(&build_projection(unique_ty, nonnull_ty), tcx), + ), + ptr_ty, + ), ); place.local = ptr_local; @@ -133,8 +132,10 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs { let (unique_ty, nonnull_ty, ptr_ty) = build_ptr_tys(tcx, boxed_ty, unique_did, nonnull_did); - new_projections - .extend_from_slice(&build_projection(unique_ty, nonnull_ty, ptr_ty)); + new_projections.extend_from_slice(&build_projection(unique_ty, nonnull_ty)); + // While we can't project into `NonNull<_>` in a basic block + // due to MCP#807, this is debug info where it's fine. + new_projections.push(PlaceElem::Field(FieldIdx::ZERO, ptr_ty)); new_projections.push(PlaceElem::Deref); } else if let Some(new_projections) = new_projections.as_mut() { // Keep building up our projections list once we've started it. diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs index e5a183bc75ce..6590702118c7 100644 --- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs +++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs @@ -351,6 +351,11 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body< { return; } + + // FIXME(typing_env): This should be able to reveal the opaques local to the + // body using the typeck results. + let typing_env = ty::TypingEnv::non_body_analysis(tcx, def_id); + // ## About BIDs in blocks ## // Track the set of blocks that contain a backwards-incompatible drop (BID) // and, for each block, the vector of locations. @@ -358,7 +363,7 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body< // We group them per-block because they tend to scheduled in the same drop ladder block. let mut bid_per_block = IndexMap::default(); let mut bid_places = UnordSet::new(); - let typing_env = ty::TypingEnv::post_analysis(tcx, def_id); + let mut ty_dropped_components = UnordMap::default(); for (block, data) in body.basic_blocks.iter_enumerated() { for (statement_index, stmt) in data.statements.iter().enumerate() { diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index ce7552e30f0f..7669a305d58d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -103,7 +103,7 @@ where |ecx| { // Const conditions must hold for the implied const bound to hold. ecx.add_goals( - GoalSource::Misc, + GoalSource::AliasBoundConstCondition, cx.const_conditions(alias_ty.def_id) .iter_instantiated(cx, alias_ty.args) .map(|trait_ref| { @@ -353,7 +353,7 @@ where ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { ecx.add_goals( - GoalSource::Misc, + GoalSource::AliasBoundConstCondition, const_conditions.into_iter().map(|trait_ref| { goal.with( cx, diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index f9cb8c9b9271..88d69eb36788 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -305,6 +305,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { Deref, Ref, Lit, + Guard, Range, Slice, Err diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 020128f29c59..701f500e4f60 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -26,7 +26,10 @@ pub(crate) fn check_crate( if items.eh_personality().is_none() { items.missing.push(LangItem::EhPersonality); } - if tcx.sess.target.os == "emscripten" && items.eh_catch_typeinfo().is_none() { + if tcx.sess.target.os == "emscripten" + && items.eh_catch_typeinfo().is_none() + && !tcx.sess.opts.unstable_opts.emscripten_wasm_eh + { items.missing.push(LangItem::EhCatchTypeinfo); } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 895259d52a7f..09648e28df47 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -448,10 +448,9 @@ pub(crate) fn encode_ty<'tcx>( if let Some(cfi_encoding) = tcx.get_attr(def_id, sym::cfi_encoding) { // Use user-defined CFI encoding for type if let Some(value_str) = cfi_encoding.value_str() { - let value_str = value_str.to_string(); - let str = value_str.trim(); - if !str.is_empty() { - s.push_str(str); + let value_str = value_str.as_str().trim(); + if !value_str.is_empty() { + s.push_str(value_str); // Don't compress user-defined builtin types (see // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-builtin and // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). @@ -459,7 +458,7 @@ pub(crate) fn encode_ty<'tcx>( "v", "w", "b", "c", "a", "h", "s", "t", "i", "j", "l", "m", "x", "y", "n", "o", "f", "d", "e", "g", "z", "Dh", ]; - if !builtin_types.contains(&str) { + if !builtin_types.contains(&value_str) { compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); } } else { diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index b68dfeffa345..d586f913335e 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -143,6 +143,7 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { | (sym::target_has_atomic_load_store, Some(_)) | (sym::target_thread_local, None) => disallow(cfg, "--target"), (sym::fmt_debug, None | Some(_)) => disallow(cfg, "-Z fmt-debug"), + (sym::emscripten_wasm_eh, None | Some(_)) => disallow(cfg, "-Z emscripten_wasm_eh"), _ => {} } } @@ -295,6 +296,10 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg { ins_none!(sym::ub_checks); } + // Nightly-only implementation detail for the `panic_unwind` and `unwind` crates. + if sess.is_nightly_build() && sess.opts.unstable_opts.emscripten_wasm_eh { + ins_none!(sym::emscripten_wasm_eh); + } ret } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 3772a4a08af0..c1fba4c513da 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -239,7 +239,8 @@ macro_rules! options { $init:expr, $parse:ident, [$dep_tracking_marker:ident], - $desc:expr) + $desc:expr + $(, deprecated_do_nothing: $dnn:literal )?) ),* ,) => ( #[derive(Clone)] @@ -280,7 +281,8 @@ macro_rules! options { } pub const $stat: OptionDescrs<$struct_name> = - &[ $( (stringify!($opt), $optmod::$opt, desc::$parse, $desc) ),* ]; + &[ $( OptionDesc{ name: stringify!($opt), setter: $optmod::$opt, + type_desc: desc::$parse, desc: $desc, is_deprecated_and_do_nothing: false $( || $dnn )? } ),* ]; mod $optmod { $( @@ -315,7 +317,27 @@ macro_rules! redirect_field { } type OptionSetter = fn(&mut O, v: Option<&str>) -> bool; -type OptionDescrs = &'static [(&'static str, OptionSetter, &'static str, &'static str)]; +type OptionDescrs = &'static [OptionDesc]; + +pub struct OptionDesc { + name: &'static str, + setter: OptionSetter, + // description for return value/type from mod desc + type_desc: &'static str, + // description for option from options table + desc: &'static str, + is_deprecated_and_do_nothing: bool, +} + +impl OptionDesc { + pub fn name(&self) -> &'static str { + self.name + } + + pub fn desc(&self) -> &'static str { + self.desc + } +} #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn build_options( @@ -333,8 +355,13 @@ fn build_options( }; let option_to_lookup = key.replace('-', "_"); - match descrs.iter().find(|(name, ..)| *name == option_to_lookup) { - Some((_, setter, type_desc, _)) => { + match descrs.iter().find(|opt_desc| opt_desc.name == option_to_lookup) { + Some(OptionDesc { name: _, setter, type_desc, desc, is_deprecated_and_do_nothing }) => { + if *is_deprecated_and_do_nothing { + // deprecation works for prefixed options only + assert!(!prefix.is_empty()); + early_dcx.early_warn(format!("`-{prefix} {key}`: {desc}")); + } if !setter(&mut op, value) { match value { None => early_dcx.early_fatal( @@ -1546,7 +1573,8 @@ options! { // tidy-alphabetical-start #[rustc_lint_opt_deny_field_access("documented to do nothing")] ar: String = (String::new(), parse_string, [UNTRACKED], - "this option is deprecated and does nothing"), + "this option is deprecated and does nothing", + deprecated_do_nothing: true), #[rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field")] code_model: Option = (None, parse_code_model, [TRACKED], "choose the code model to use (`rustc --print code-models` for details)"), @@ -1578,9 +1606,10 @@ options! { incremental: Option = (None, parse_opt_string, [UNTRACKED], "enable incremental compilation"), #[rustc_lint_opt_deny_field_access("documented to do nothing")] - inline_threshold: Option = (None, parse_opt_number, [TRACKED], + inline_threshold: Option = (None, parse_opt_number, [UNTRACKED], "this option is deprecated and does nothing \ - (consider using `-Cllvm-args=--inline-threshold=...`)"), + (consider using `-Cllvm-args=--inline-threshold=...`)", + deprecated_do_nothing: true), #[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")] instrument_coverage: InstrumentCoverage = (InstrumentCoverage::No, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage reports \ @@ -1616,7 +1645,8 @@ options! { "disable the use of the redzone"), #[rustc_lint_opt_deny_field_access("documented to do nothing")] no_stack_check: bool = (false, parse_no_value, [UNTRACKED], - "this option is deprecated and does nothing"), + "this option is deprecated and does nothing", + deprecated_do_nothing: true), no_vectorize_loops: bool = (false, parse_no_value, [TRACKED], "disable loop vectorization optimization passes"), no_vectorize_slp: bool = (false, parse_no_value, [TRACKED], @@ -1771,6 +1801,8 @@ options! { "emit a section containing stack size metadata (default: no)"), emit_thin_lto: bool = (true, parse_bool, [TRACKED], "emit the bc module with thin LTO info (default: yes)"), + emscripten_wasm_eh: bool = (false, parse_bool, [TRACKED], + "Use WebAssembly error handling for wasm32-unknown-emscripten"), enforce_type_length_limit: bool = (false, parse_bool, [TRACKED], "enforce the type length limit when monomorphizing instances in codegen"), export_executable_symbols: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index bdfbfb1e38dd..0dcf38e34936 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -570,6 +570,7 @@ symbols! { cfg_autodiff_fallback, cfg_boolean_literals, cfg_doctest, + cfg_emscripten_wasm_eh, cfg_eval, cfg_fmt_debug, cfg_hide, @@ -823,6 +824,7 @@ symbols! { emit_enum_variant_arg, emit_struct, emit_struct_field, + emscripten_wasm_eh, enable, encode, end, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 0d6d8488a23c..333ea0214eb3 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -8,6 +8,7 @@ use rustc_middle::bug; use rustc_middle::ty::print::{PrettyPrinter, Print, PrintError, Printer}; use rustc_middle::ty::{ self, GenericArg, GenericArgKind, Instance, ReifyReason, Ty, TyCtxt, TypeVisitableExt, + TypingEnv, }; use tracing::debug; @@ -383,14 +384,26 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { &mut self, impl_def_id: DefId, args: &'tcx [GenericArg<'tcx>], - mut self_ty: Ty<'tcx>, - mut impl_trait_ref: Option>, ) -> Result<(), PrintError> { - let mut typing_env = ty::TypingEnv::post_analysis(self.tcx, impl_def_id); - if !args.is_empty() { - typing_env.param_env = - ty::EarlyBinder::bind(typing_env.param_env).instantiate(self.tcx, args); - } + let self_ty = self.tcx.type_of(impl_def_id); + let impl_trait_ref = self.tcx.impl_trait_ref(impl_def_id); + let (typing_env, mut self_ty, mut impl_trait_ref) = + if self.tcx.generics_of(impl_def_id).count() <= args.len() { + ( + TypingEnv::fully_monomorphized(), + self_ty.instantiate(self.tcx, args), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate(self.tcx, args)), + ) + } else { + // We are probably printing a nested item inside of an impl. + // Use the identity substitutions for the impl. We also need + // a well-formed param-env, so let's use post-analysis. + ( + TypingEnv::post_analysis(self.tcx, impl_def_id), + self_ty.instantiate_identity(), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate_identity()), + ) + }; match &mut impl_trait_ref { Some(impl_trait_ref) => { @@ -403,7 +416,7 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { } } - self.default_print_impl_path(impl_def_id, args, self_ty, impl_trait_ref) + self.default_print_impl_path(impl_def_id, self_ty, impl_trait_ref) } } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 0ca47eba5e81..b77ad209e2bd 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -14,8 +14,8 @@ use rustc_middle::bug; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::print::{Print, PrintError, Printer}; use rustc_middle::ty::{ - self, EarlyBinder, FloatTy, GenericArg, GenericArgKind, Instance, IntTy, ReifyReason, Ty, - TyCtxt, TypeVisitable, TypeVisitableExt, UintTy, + self, FloatTy, GenericArg, GenericArgKind, Instance, IntTy, ReifyReason, Ty, TyCtxt, + TypeVisitable, TypeVisitableExt, UintTy, }; use rustc_span::kw; @@ -227,17 +227,29 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { &mut self, impl_def_id: DefId, args: &'tcx [GenericArg<'tcx>], - mut self_ty: Ty<'tcx>, - mut impl_trait_ref: Option>, ) -> Result<(), PrintError> { let key = self.tcx.def_key(impl_def_id); let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id }; - let mut typing_env = ty::TypingEnv::post_analysis(self.tcx, impl_def_id); - if !args.is_empty() { - typing_env.param_env = - EarlyBinder::bind(typing_env.param_env).instantiate(self.tcx, args); - } + let self_ty = self.tcx.type_of(impl_def_id); + let impl_trait_ref = self.tcx.impl_trait_ref(impl_def_id); + let (typing_env, mut self_ty, mut impl_trait_ref) = + if self.tcx.generics_of(impl_def_id).count() <= args.len() { + ( + ty::TypingEnv::fully_monomorphized(), + self_ty.instantiate(self.tcx, args), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate(self.tcx, args)), + ) + } else { + // We are probably printing a nested item inside of an impl. + // Use the identity substitutions for the impl. We also need + // a well-formed param-env, so let's use post-analysis. + ( + ty::TypingEnv::post_analysis(self.tcx, impl_def_id), + self_ty.instantiate_identity(), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate_identity()), + ) + }; match &mut impl_trait_ref { Some(impl_trait_ref) => { diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index bfe6f4441471..0dc1d795a8ed 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1656,6 +1656,7 @@ supported_targets! { ("loongarch64-unknown-linux-gnu", loongarch64_unknown_linux_gnu), ("loongarch64-unknown-linux-musl", loongarch64_unknown_linux_musl), ("m68k-unknown-linux-gnu", m68k_unknown_linux_gnu), + ("m68k-unknown-none-elf", m68k_unknown_none_elf), ("csky-unknown-linux-gnuabiv2", csky_unknown_linux_gnuabiv2), ("csky-unknown-linux-gnuabiv2hf", csky_unknown_linux_gnuabiv2hf), ("mips-unknown-linux-gnu", mips_unknown_linux_gnu), @@ -2653,10 +2654,6 @@ impl TargetOptions { pub(crate) fn has_feature(&self, search_feature: &str) -> bool { self.features.split(',').any(|f| f.strip_prefix('+').is_some_and(|f| f == search_feature)) } - - pub(crate) fn has_neg_feature(&self, search_feature: &str) -> bool { - self.features.split(',').any(|f| f.strip_prefix('-').is_some_and(|f| f == search_feature)) - } } impl Default for TargetOptions { @@ -3203,7 +3200,8 @@ impl Target { check_matches!( &*self.llvm_abiname, "ilp32" | "ilp32f" | "ilp32d" | "ilp32e", - "invalid RISC-V ABI name" + "invalid RISC-V ABI name: {}", + self.llvm_abiname, ); } "riscv64" => { @@ -3211,7 +3209,8 @@ impl Target { check_matches!( &*self.llvm_abiname, "lp64" | "lp64f" | "lp64d" | "lp64e", - "invalid RISC-V ABI name" + "invalid RISC-V ABI name: {}", + self.llvm_abiname, ); } "arm" => { @@ -3245,6 +3244,26 @@ impl Target { )); } } + // Check that we don't mis-set any of the ABI-relevant features. + let abi_feature_constraints = self.abi_required_features(); + for feat in abi_feature_constraints.required { + // The feature might be enabled by default so we can't *require* it to show up. + // But it must not be *disabled*. + if features_disabled.contains(feat) { + return Err(format!( + "target feature `{feat}` is required by the ABI but gets disabled in target spec" + )); + } + } + for feat in abi_feature_constraints.incompatible { + // The feature might be disabled by default so we can't *require* it to show up. + // But it must not be *enabled*. + if features_enabled.contains(feat) { + return Err(format!( + "target feature `{feat}` is incompatible with the ABI but gets enabled in target spec" + )); + } + } } Ok(()) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs index d5e78d030760..ac53cbaecceb 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: "aarch64".into(), options: TargetOptions { - features: "+v8a".into(), + features: "+v8a,+reserve-x18".into(), max_atomic_width: Some(128), stack_probes: StackProbeType::Inline, ..base::vxworks::opts() diff --git a/compiler/rustc_target/src/spec/targets/m68k_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/m68k_unknown_none_elf.rs new file mode 100644 index 000000000000..6b8b7c6ae003 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/m68k_unknown_none_elf.rs @@ -0,0 +1,33 @@ +use crate::abi::Endian; +use crate::spec::{CodeModel, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub(crate) fn target() -> Target { + let options = TargetOptions { + cpu: "M68010".into(), + max_atomic_width: None, + endian: Endian::Big, + // LLD currently does not have support for M68k + linker: Some("m68k-linux-gnu-ld".into()), + panic_strategy: PanicStrategy::Abort, + code_model: Some(CodeModel::Medium), + has_rpath: false, + // should be soft-float + llvm_floatabi: None, + relocation_model: RelocModel::Static, + ..Default::default() + }; + + Target { + llvm_target: "m68k".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Motorola 680x0".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 32, + data_layout: "E-m:e-p:32:16:32-i8:8:8-i16:16:16-i32:16:32-n8:16:32-a:0:16-S16".into(), + arch: "m68k".into(), + options, + } +} diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 5372437b0d2a..9fd07c8634aa 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_span::{Symbol, sym}; -use crate::spec::Target; +use crate::spec::{FloatAbi, Target}; /// Features that control behaviour of rustc, rather than the codegen. /// These exist globally and are not in the target-specific lists below. @@ -17,60 +17,34 @@ pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"]; /// Stability information for target features. -/// `Toggleability` is the type storing whether (un)stable features can be toggled: -/// this is initially a function since it can depend on `Target`, but for stable hashing -/// it needs to be something hashable to we have to make the type generic. -#[derive(Debug, Clone)] -pub enum Stability { +#[derive(Debug, Copy, Clone)] +pub enum Stability { /// This target feature is stable, it can be used in `#[target_feature]` and /// `#[cfg(target_feature)]`. - Stable { - /// When enabling/disabling the feature via `-Ctarget-feature` or `#[target_feature]`, - /// determine if that is allowed. - allow_toggle: Toggleability, - }, + Stable, /// This target feature is unstable. It is only present in `#[cfg(target_feature)]` on /// nightly and using it in `#[target_feature]` requires enabling the given nightly feature. - Unstable { + Unstable( /// This must be a *language* feature, or else rustc will ICE when reporting a missing /// feature gate! - nightly_feature: Symbol, - /// See `Stable::allow_toggle` comment above. - allow_toggle: Toggleability, - }, + Symbol, + ), /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be /// set in the target spec. It is never set in `cfg(target_feature)`. Used in - /// particular for features that change the floating-point ABI. + /// particular for features are actually ABI configuration flags (not all targets are as nice as + /// RISC-V and have an explicit way to set the ABI separate from target features). Forbidden { reason: &'static str }, } +use Stability::*; -/// Returns `Ok` if the toggle is allowed, `Err` with an explanation of not. -/// The `bool` indicates whether the feature is being enabled (`true`) or disabled. -pub type AllowToggleUncomputed = fn(&Target, bool) -> Result<(), &'static str>; - -/// The computed result of whether a feature can be enabled/disabled on the current target. -#[derive(Debug, Clone)] -pub struct AllowToggleComputed { - enable: Result<(), &'static str>, - disable: Result<(), &'static str>, -} - -/// `Stability` where `allow_toggle` has not been computed yet. -pub type StabilityUncomputed = Stability; -/// `Stability` where `allow_toggle` has already been computed. -pub type StabilityComputed = Stability; - -impl> HashStable for Stability { +impl HashStable for Stability { #[inline] fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { std::mem::discriminant(self).hash_stable(hcx, hasher); match self { - Stability::Stable { allow_toggle } => { - allow_toggle.hash_stable(hcx, hasher); - } - Stability::Unstable { nightly_feature, allow_toggle } => { + Stability::Stable => {} + Stability::Unstable(nightly_feature) => { nightly_feature.hash_stable(hcx, hasher); - allow_toggle.hash_stable(hcx, hasher); } Stability::Forbidden { reason } => { reason.hash_stable(hcx, hasher); @@ -79,16 +53,7 @@ impl> HashStable for Stability HashStable for AllowToggleComputed { - #[inline] - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - let AllowToggleComputed { enable, disable } = self; - enable.hash_stable(hcx, hasher); - disable.hash_stable(hcx, hasher); - } -} - -impl Stability { +impl Stability { /// Returns whether the feature can be used in `cfg(target_feature)` ever. /// (It might still be nightly-only even if this returns `true`, so make sure to also check /// `requires_nightly`.) @@ -106,59 +71,23 @@ impl Stability { /// - for `cfg(target_feature)`, check `in_cfg` pub fn requires_nightly(&self) -> Option { match *self { - Stability::Unstable { nightly_feature, .. } => Some(nightly_feature), + Stability::Unstable(nightly_feature) => Some(nightly_feature), Stability::Stable { .. } => None, Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"), } } -} -impl StabilityUncomputed { - pub fn compute_toggleability(&self, target: &Target) -> StabilityComputed { - use Stability::*; - let compute = |f: AllowToggleUncomputed| AllowToggleComputed { - enable: f(target, true), - disable: f(target, false), - }; - match *self { - Stable { allow_toggle } => Stable { allow_toggle: compute(allow_toggle) }, - Unstable { nightly_feature, allow_toggle } => { - Unstable { nightly_feature, allow_toggle: compute(allow_toggle) } - } - Forbidden { reason } => Forbidden { reason }, - } - } - - pub fn toggle_allowed(&self, target: &Target, enable: bool) -> Result<(), &'static str> { - use Stability::*; - match *self { - Stable { allow_toggle } => allow_toggle(target, enable), - Unstable { allow_toggle, .. } => allow_toggle(target, enable), - Forbidden { reason } => Err(reason), - } - } -} - -impl StabilityComputed { /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`. /// (It might still be nightly-only even if this returns `true`, so make sure to also check /// `requires_nightly`.) - pub fn toggle_allowed(&self, enable: bool) -> Result<(), &'static str> { - let allow_toggle = match self { - Stability::Stable { allow_toggle } => allow_toggle, - Stability::Unstable { allow_toggle, .. } => allow_toggle, - Stability::Forbidden { reason } => return Err(reason), - }; - if enable { allow_toggle.enable } else { allow_toggle.disable } + pub fn toggle_allowed(&self) -> Result<(), &'static str> { + match self { + Stability::Forbidden { reason } => Err(reason), + _ => Ok(()), + } } } -// Constructors for the list below, defaulting to "always allow toggle". -const STABLE: StabilityUncomputed = Stability::Stable { allow_toggle: |_target, _enable| Ok(()) }; -const fn unstable(nightly_feature: Symbol) -> StabilityUncomputed { - Stability::Unstable { nightly_feature, allow_toggle: |_target, _enable| Ok(()) } -} - // Here we list target features that rustc "understands": they can be used in `#[target_feature]` // and `#[cfg(target_feature)]`. They also do not trigger any warnings when used with // `-Ctarget-feature`. @@ -204,218 +133,187 @@ const fn unstable(nightly_feature: Symbol) -> StabilityUncomputed { // Both of these are also applied transitively. type ImpliedFeatures = &'static [&'static str]; -const ARM_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("aclass", unstable(sym::arm_target_feature), &[]), - ("aes", unstable(sym::arm_target_feature), &["neon"]), - ("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"]), - ( - "fpregs", - Stability::Unstable { - nightly_feature: sym::arm_target_feature, - allow_toggle: |target: &Target, _enable| { - // Only allow toggling this if the target has `soft-float` set. With `soft-float`, - // `fpregs` isn't needed so changing it cannot affect the ABI. - if target.has_feature("soft-float") { - Ok(()) - } else { - Err("unsound on hard-float targets because it changes float ABI") - } - }, - }, - &[], - ), - ("i8mm", unstable(sym::arm_target_feature), &["neon"]), - ("mclass", unstable(sym::arm_target_feature), &[]), - ("neon", unstable(sym::arm_target_feature), &["vfp3"]), - ("rclass", unstable(sym::arm_target_feature), &[]), - ("sha2", unstable(sym::arm_target_feature), &["neon"]), - ("soft-float", Stability::Forbidden { reason: "unsound because it changes float ABI" }, &[]), + ("aclass", Unstable(sym::arm_target_feature), &[]), + ("aes", Unstable(sym::arm_target_feature), &["neon"]), + ("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"]), + ("fpregs", Unstable(sym::arm_target_feature), &[]), + ("i8mm", Unstable(sym::arm_target_feature), &["neon"]), + ("mclass", Unstable(sym::arm_target_feature), &[]), + ("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"]), - ("v6t2", unstable(sym::arm_target_feature), &["v6k", "thumb2"]), - ("v7", unstable(sym::arm_target_feature), &["v6t2"]), - ("v8", unstable(sym::arm_target_feature), &["v7"]), - ("vfp2", unstable(sym::arm_target_feature), &[]), - ("vfp3", unstable(sym::arm_target_feature), &["vfp2", "d32"]), - ("vfp4", unstable(sym::arm_target_feature), &["vfp3"]), - ("virtualization", unstable(sym::arm_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"]), + ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "thumb2"]), + ("v7", Unstable(sym::arm_target_feature), &["v6t2"]), + ("v8", Unstable(sym::arm_target_feature), &["v7"]), + ("vfp2", Unstable(sym::arm_target_feature), &[]), + ("vfp3", Unstable(sym::arm_target_feature), &["vfp2", "d32"]), + ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), + ("virtualization", Unstable(sym::arm_target_feature), &[]), // tidy-alphabetical-end ]; -const AARCH64_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start // FEAT_AES & FEAT_PMULL - ("aes", STABLE, &["neon"]), + ("aes", Stable, &["neon"]), // FEAT_BF16 - ("bf16", STABLE, &[]), + ("bf16", Stable, &[]), // FEAT_BTI - ("bti", STABLE, &[]), + ("bti", Stable, &[]), // FEAT_CRC - ("crc", STABLE, &[]), + ("crc", Stable, &[]), // FEAT_CSSC - ("cssc", unstable(sym::aarch64_unstable_target_feature), &[]), + ("cssc", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_DIT - ("dit", STABLE, &[]), + ("dit", Stable, &[]), // FEAT_DotProd - ("dotprod", STABLE, &["neon"]), + ("dotprod", Stable, &["neon"]), // FEAT_DPB - ("dpb", STABLE, &[]), + ("dpb", Stable, &[]), // FEAT_DPB2 - ("dpb2", STABLE, &["dpb"]), + ("dpb2", Stable, &["dpb"]), // FEAT_ECV - ("ecv", unstable(sym::aarch64_unstable_target_feature), &[]), + ("ecv", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_F32MM - ("f32mm", STABLE, &["sve"]), + ("f32mm", Stable, &["sve"]), // FEAT_F64MM - ("f64mm", STABLE, &["sve"]), + ("f64mm", Stable, &["sve"]), // FEAT_FAMINMAX - ("faminmax", unstable(sym::aarch64_unstable_target_feature), &[]), + ("faminmax", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_FCMA - ("fcma", STABLE, &["neon"]), + ("fcma", Stable, &["neon"]), // FEAT_FHM - ("fhm", STABLE, &["fp16"]), + ("fhm", Stable, &["fp16"]), // FEAT_FLAGM - ("flagm", STABLE, &[]), + ("flagm", Stable, &[]), // FEAT_FLAGM2 - ("flagm2", unstable(sym::aarch64_unstable_target_feature), &[]), + ("flagm2", Unstable(sym::aarch64_unstable_target_feature), &[]), + // We forbid directly toggling just `fp-armv8`; it must be toggled with `neon`. ("fp-armv8", Stability::Forbidden { reason: "Rust ties `fp-armv8` to `neon`" }, &[]), // FEAT_FP16 // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 - ("fp16", STABLE, &["neon"]), + ("fp16", Stable, &["neon"]), // FEAT_FP8 - ("fp8", unstable(sym::aarch64_unstable_target_feature), &["faminmax", "lut", "bf16"]), + ("fp8", Unstable(sym::aarch64_unstable_target_feature), &["faminmax", "lut", "bf16"]), // FEAT_FP8DOT2 - ("fp8dot2", unstable(sym::aarch64_unstable_target_feature), &["fp8dot4"]), + ("fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["fp8dot4"]), // FEAT_FP8DOT4 - ("fp8dot4", unstable(sym::aarch64_unstable_target_feature), &["fp8fma"]), + ("fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["fp8fma"]), // FEAT_FP8FMA - ("fp8fma", unstable(sym::aarch64_unstable_target_feature), &["fp8"]), + ("fp8fma", Unstable(sym::aarch64_unstable_target_feature), &["fp8"]), // FEAT_FRINTTS - ("frintts", STABLE, &[]), + ("frintts", Stable, &[]), // FEAT_HBC - ("hbc", unstable(sym::aarch64_unstable_target_feature), &[]), + ("hbc", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_I8MM - ("i8mm", STABLE, &[]), + ("i8mm", Stable, &[]), // FEAT_JSCVT // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 - ("jsconv", STABLE, &["neon"]), + ("jsconv", Stable, &["neon"]), // FEAT_LOR - ("lor", STABLE, &[]), + ("lor", Stable, &[]), // FEAT_LSE - ("lse", STABLE, &[]), + ("lse", Stable, &[]), // FEAT_LSE128 - ("lse128", unstable(sym::aarch64_unstable_target_feature), &["lse"]), + ("lse128", Unstable(sym::aarch64_unstable_target_feature), &["lse"]), // FEAT_LSE2 - ("lse2", unstable(sym::aarch64_unstable_target_feature), &[]), + ("lse2", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_LUT - ("lut", unstable(sym::aarch64_unstable_target_feature), &[]), + ("lut", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_MOPS - ("mops", unstable(sym::aarch64_unstable_target_feature), &[]), + ("mops", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_MTE & FEAT_MTE2 - ("mte", STABLE, &[]), + ("mte", Stable, &[]), // FEAT_AdvSimd & FEAT_FP - ( - "neon", - Stability::Stable { - allow_toggle: |target, enable| { - if target.abi == "softfloat" { - // `neon` has no ABI implications for softfloat targets, we can allow this. - Ok(()) - } else if enable - && !target.has_neg_feature("fp-armv8") - && !target.has_neg_feature("neon") - { - // neon is enabled by default, and has not been disabled, so enabling it again - // is redundant and we can permit it. Forbidding this would be a breaking change - // since this feature is stable. - Ok(()) - } else { - Err("unsound on hard-float targets because it changes float ABI") - } - }, - }, - &[], - ), + ("neon", Stable, &[]), // FEAT_PAUTH (address authentication) - ("paca", STABLE, &[]), + ("paca", Stable, &[]), // FEAT_PAUTH (generic authentication) - ("pacg", STABLE, &[]), + ("pacg", Stable, &[]), // FEAT_PAN - ("pan", STABLE, &[]), + ("pan", Stable, &[]), // FEAT_PAuth_LR - ("pauth-lr", unstable(sym::aarch64_unstable_target_feature), &[]), + ("pauth-lr", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_PMUv3 - ("pmuv3", STABLE, &[]), + ("pmuv3", Stable, &[]), // FEAT_RNG - ("rand", STABLE, &[]), + ("rand", Stable, &[]), // FEAT_RAS & FEAT_RASv1p1 - ("ras", STABLE, &[]), + ("ras", Stable, &[]), // FEAT_LRCPC - ("rcpc", STABLE, &[]), + ("rcpc", Stable, &[]), // FEAT_LRCPC2 - ("rcpc2", STABLE, &["rcpc"]), + ("rcpc2", Stable, &["rcpc"]), // FEAT_LRCPC3 - ("rcpc3", unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), + ("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), // FEAT_RDM - ("rdm", STABLE, &["neon"]), + ("rdm", Stable, &["neon"]), // This is needed for inline assembly, but shouldn't be stabilized as-is // since it should be enabled globally using -Zfixed-x18, not // #[target_feature]. // Note that cfg(target_feature = "reserve-x18") is currently not set for // targets that reserve x18 by default. - ("reserve-x18", unstable(sym::aarch64_unstable_target_feature), &[]), + ("reserve-x18", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_SB - ("sb", STABLE, &[]), + ("sb", Stable, &[]), // FEAT_SHA1 & FEAT_SHA256 - ("sha2", STABLE, &["neon"]), + ("sha2", Stable, &["neon"]), // FEAT_SHA512 & FEAT_SHA3 - ("sha3", STABLE, &["sha2"]), + ("sha3", Stable, &["sha2"]), // FEAT_SM3 & FEAT_SM4 - ("sm4", STABLE, &["neon"]), + ("sm4", Stable, &["neon"]), // FEAT_SME - ("sme", unstable(sym::aarch64_unstable_target_feature), &["bf16"]), + ("sme", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]), // FEAT_SME_B16B16 - ("sme-b16b16", unstable(sym::aarch64_unstable_target_feature), &["bf16", "sme2", "sve-b16b16"]), + ("sme-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16", "sme2", "sve-b16b16"]), // FEAT_SME_F16F16 - ("sme-f16f16", unstable(sym::aarch64_unstable_target_feature), &["sme2"]), + ("sme-f16f16", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]), // FEAT_SME_F64F64 - ("sme-f64f64", unstable(sym::aarch64_unstable_target_feature), &["sme"]), + ("sme-f64f64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), // FEAT_SME_F8F16 - ("sme-f8f16", unstable(sym::aarch64_unstable_target_feature), &["sme-f8f32"]), + ("sme-f8f16", Unstable(sym::aarch64_unstable_target_feature), &["sme-f8f32"]), // FEAT_SME_F8F32 - ("sme-f8f32", unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), + ("sme-f8f32", Unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), // FEAT_SME_FA64 - ("sme-fa64", unstable(sym::aarch64_unstable_target_feature), &["sme", "sve2"]), + ("sme-fa64", Unstable(sym::aarch64_unstable_target_feature), &["sme", "sve2"]), // FEAT_SME_I16I64 - ("sme-i16i64", unstable(sym::aarch64_unstable_target_feature), &["sme"]), + ("sme-i16i64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), // FEAT_SME_LUTv2 - ("sme-lutv2", unstable(sym::aarch64_unstable_target_feature), &[]), + ("sme-lutv2", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_SME2 - ("sme2", unstable(sym::aarch64_unstable_target_feature), &["sme"]), + ("sme2", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), // FEAT_SME2p1 - ("sme2p1", unstable(sym::aarch64_unstable_target_feature), &["sme2"]), + ("sme2p1", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]), // FEAT_SPE - ("spe", STABLE, &[]), + ("spe", Stable, &[]), // FEAT_SSBS & FEAT_SSBS2 - ("ssbs", STABLE, &[]), + ("ssbs", Stable, &[]), // FEAT_SSVE_FP8FDOT2 - ("ssve-fp8dot2", unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8dot4"]), + ("ssve-fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8dot4"]), // FEAT_SSVE_FP8FDOT4 - ("ssve-fp8dot4", unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8fma"]), + ("ssve-fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8fma"]), // FEAT_SSVE_FP8FMA - ("ssve-fp8fma", unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), + ("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 // @@ -423,46 +321,46 @@ const AARCH64_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // 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"]), + ("sve", Stable, &["neon"]), // FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions) - ("sve-b16b16", unstable(sym::aarch64_unstable_target_feature), &["bf16"]), + ("sve-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]), // FEAT_SVE2 - ("sve2", STABLE, &["sve"]), + ("sve2", Stable, &["sve"]), // FEAT_SVE_AES & FEAT_SVE_PMULL128 - ("sve2-aes", STABLE, &["sve2", "aes"]), + ("sve2-aes", Stable, &["sve2", "aes"]), // FEAT_SVE2_BitPerm - ("sve2-bitperm", STABLE, &["sve2"]), + ("sve2-bitperm", Stable, &["sve2"]), // FEAT_SVE2_SHA3 - ("sve2-sha3", STABLE, &["sve2", "sha3"]), + ("sve2-sha3", Stable, &["sve2", "sha3"]), // FEAT_SVE2_SM4 - ("sve2-sm4", STABLE, &["sve2", "sm4"]), + ("sve2-sm4", Stable, &["sve2", "sm4"]), // FEAT_SVE2p1 - ("sve2p1", unstable(sym::aarch64_unstable_target_feature), &["sve2"]), + ("sve2p1", Unstable(sym::aarch64_unstable_target_feature), &["sve2"]), // FEAT_TME - ("tme", STABLE, &[]), - ("v8.1a", unstable(sym::aarch64_ver_target_feature), &[ + ("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", 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", "sve2"]), + ("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", "sve2"]), // FEAT_VHE - ("vh", STABLE, &[]), + ("vh", Stable, &[]), // FEAT_WFxT - ("wfxt", unstable(sym::aarch64_unstable_target_feature), &[]), + ("wfxt", Unstable(sym::aarch64_unstable_target_feature), &[]), // tidy-alphabetical-end ]; @@ -470,337 +368,260 @@ const AARCH64_TIED_FEATURES: &[&[&str]] = &[ &["paca", "pacg"], // Together these represent `pauth` in LLVM ]; -const X86_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("adx", STABLE, &[]), - ("aes", STABLE, &["sse2"]), - ("amx-bf16", unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-complex", 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-tile", unstable(sym::x86_amx_intrinsics), &[]), - ("avx", STABLE, &["sse4.2"]), - ("avx2", STABLE, &["avx"]), - ("avx512bf16", unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bitalg", unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bw", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512cd", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512dq", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512f", unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), - ("avx512fp16", unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), - ("avx512ifma", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vbmi", unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vbmi2", unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vl", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vnni", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vp2intersect", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vpopcntdq", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avxifma", unstable(sym::avx512_target_feature), &["avx2"]), - ("avxneconvert", unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnni", unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint16", unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint8", unstable(sym::avx512_target_feature), &["avx2"]), - ("bmi1", STABLE, &[]), - ("bmi2", STABLE, &[]), - ("cmpxchg16b", STABLE, &[]), - ("ermsb", unstable(sym::ermsb_target_feature), &[]), - ("f16c", STABLE, &["avx"]), - ("fma", STABLE, &["avx"]), - ("fxsr", STABLE, &[]), - ("gfni", unstable(sym::avx512_target_feature), &["sse2"]), - ("lahfsahf", unstable(sym::lahfsahf_target_feature), &[]), - ("lzcnt", STABLE, &[]), - ("movbe", STABLE, &[]), - ("pclmulqdq", STABLE, &["sse2"]), - ("popcnt", STABLE, &[]), - ("prfchw", unstable(sym::prfchw_target_feature), &[]), - ("rdrand", STABLE, &[]), - ("rdseed", STABLE, &[]), - ("rtm", unstable(sym::rtm_target_feature), &[]), - ("sha", STABLE, &["sse2"]), - ("sha512", unstable(sym::sha512_sm_x86), &["avx2"]), - ("sm3", unstable(sym::sha512_sm_x86), &["avx"]), - ("sm4", unstable(sym::sha512_sm_x86), &["avx2"]), + ("adx", Stable, &[]), + ("aes", Stable, &["sse2"]), + ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-complex", 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-tile", Unstable(sym::x86_amx_intrinsics), &[]), + ("avx", Stable, &["sse4.2"]), + ("avx2", Stable, &["avx"]), + ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), + ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), + ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]), + ("bmi1", Stable, &[]), + ("bmi2", Stable, &[]), + ("cmpxchg16b", Stable, &[]), + ("ermsb", Unstable(sym::ermsb_target_feature), &[]), + ("f16c", Stable, &["avx"]), + ("fma", Stable, &["avx"]), + ("fxsr", Stable, &[]), + ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]), + ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), + ("lzcnt", Stable, &[]), + ("movbe", Stable, &[]), + ("pclmulqdq", Stable, &["sse2"]), + ("popcnt", Stable, &[]), + ("prfchw", Unstable(sym::prfchw_target_feature), &[]), + ("rdrand", Stable, &[]), + ("rdseed", Stable, &[]), + ("rtm", Unstable(sym::rtm_target_feature), &[]), + ("sha", Stable, &["sse2"]), + ("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]), + ("sm3", Unstable(sym::sha512_sm_x86), &["avx"]), + ("sm4", Unstable(sym::sha512_sm_x86), &["avx2"]), ("soft-float", Stability::Forbidden { reason: "unsound because it changes float ABI" }, &[]), - ("sse", STABLE, &[]), - ("sse2", STABLE, &["sse"]), - ("sse3", STABLE, &["sse2"]), - ("sse4.1", STABLE, &["ssse3"]), - ("sse4.2", STABLE, &["sse4.1"]), - ("sse4a", unstable(sym::sse4a_target_feature), &["sse3"]), - ("ssse3", STABLE, &["sse3"]), - ("tbm", unstable(sym::tbm_target_feature), &[]), - ("vaes", unstable(sym::avx512_target_feature), &["avx2", "aes"]), - ("vpclmulqdq", unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), - ( - "x87", - Stability::Unstable { - nightly_feature: sym::x87_target_feature, - allow_toggle: |target: &Target, _enable| { - // Only allow toggling this if the target has `soft-float` set. With `soft-float`, - // `fpregs` isn't needed so changing it cannot affect the ABI. - if target.has_feature("soft-float") { - Ok(()) - } else { - Err("unsound on hard-float targets because it changes float ABI") - } - }, - }, - &[], - ), - ("xop", unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), - ("xsave", STABLE, &[]), - ("xsavec", STABLE, &["xsave"]), - ("xsaveopt", STABLE, &["xsave"]), - ("xsaves", STABLE, &["xsave"]), + ("sse", Stable, &[]), + ("sse2", Stable, &["sse"]), + ("sse3", Stable, &["sse2"]), + ("sse4.1", Stable, &["ssse3"]), + ("sse4.2", Stable, &["sse4.1"]), + ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]), + ("ssse3", Stable, &["sse3"]), + ("tbm", Unstable(sym::tbm_target_feature), &[]), + ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]), + ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), + ("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, StabilityUncomputed, ImpliedFeatures)] = &[ +const HEXAGON_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("hvx", unstable(sym::hexagon_target_feature), &[]), - ("hvx-length128b", unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvx", Unstable(sym::hexagon_target_feature), &[]), + ("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]), // tidy-alphabetical-end ]; -const POWERPC_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const POWERPC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("altivec", unstable(sym::powerpc_target_feature), &[]), - ("partword-atomics", unstable(sym::powerpc_target_feature), &[]), - ("power10-vector", unstable(sym::powerpc_target_feature), &["power9-vector"]), - ("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"]), - ("quadword-atomics", unstable(sym::powerpc_target_feature), &[]), - ("vsx", unstable(sym::powerpc_target_feature), &["altivec"]), + ("altivec", Unstable(sym::powerpc_target_feature), &[]), + ("partword-atomics", Unstable(sym::powerpc_target_feature), &[]), + ("power10-vector", Unstable(sym::powerpc_target_feature), &["power9-vector"]), + ("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"]), + ("quadword-atomics", Unstable(sym::powerpc_target_feature), &[]), + ("vsx", Unstable(sym::powerpc_target_feature), &["altivec"]), // tidy-alphabetical-end ]; -const MIPS_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const MIPS_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("fp64", unstable(sym::mips_target_feature), &[]), - ("msa", unstable(sym::mips_target_feature), &[]), - ("virt", unstable(sym::mips_target_feature), &[]), + ("fp64", Unstable(sym::mips_target_feature), &[]), + ("msa", Unstable(sym::mips_target_feature), &[]), + ("virt", Unstable(sym::mips_target_feature), &[]), // tidy-alphabetical-end ]; -const RISCV_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("a", STABLE, &["zaamo", "zalrsc"]), - ("c", STABLE, &[]), - ( - "d", - Stability::Unstable { - nightly_feature: sym::riscv_target_feature, - allow_toggle: |target, enable| match &*target.llvm_abiname { - "ilp32d" | "lp64d" if !enable => { - // The ABI requires the `d` feature, so it cannot be disabled. - Err("feature is required by ABI") - } - "ilp32e" if enable => { - // ilp32e is incompatible with features that need aligned load/stores > 32 bits, - // like `d`. - Err("feature is incompatible with ABI") - } - _ => Ok(()), - }, - }, - &["f"], - ), - ( - "e", - Stability::Unstable { - // Given that this is a negative feature, consider this before stabilizing: - // does it really make sense to enable this feature in an individual - // function with `#[target_feature]`? - nightly_feature: sym::riscv_target_feature, - allow_toggle: |target, enable| { - match &*target.llvm_abiname { - _ if !enable => { - // Disabling this feature means we can use more registers (x16-x31). - // The "e" ABIs treat them as caller-save, so it is safe to use them only - // in some parts of a program while the rest doesn't know they even exist. - // On other ABIs, the feature is already disabled anyway. - Ok(()) - } - "ilp32e" | "lp64e" => { - // Embedded ABIs should already have the feature anyway, it's fine to enable - // it again from an ABI perspective. - Ok(()) - } - _ => { - // *Not* an embedded ABI. Enabling `e` is invalid. - Err("feature is incompatible with ABI") - } - } - }, - }, - &[], - ), - ( - "f", - Stability::Unstable { - nightly_feature: sym::riscv_target_feature, - allow_toggle: |target, enable| { - match &*target.llvm_abiname { - "ilp32f" | "ilp32d" | "lp64f" | "lp64d" if !enable => { - // The ABI requires the `f` feature, so it cannot be disabled. - Err("feature is required by ABI") - } - _ => Ok(()), - } - }, - }, - &[], - ), + ("a", Stable, &["zaamo", "zalrsc"]), + ("c", Stable, &[]), + ("d", Unstable(sym::riscv_target_feature), &["f"]), + ("e", Unstable(sym::riscv_target_feature), &[]), + ("f", Unstable(sym::riscv_target_feature), &[]), ( "forced-atomics", Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" }, &[], ), - ("m", STABLE, &[]), - ("relax", unstable(sym::riscv_target_feature), &[]), - ("unaligned-scalar-mem", unstable(sym::riscv_target_feature), &[]), - ("v", unstable(sym::riscv_target_feature), &[]), - ("zaamo", unstable(sym::riscv_target_feature), &[]), - ("zabha", unstable(sym::riscv_target_feature), &["zaamo"]), - ("zalrsc", unstable(sym::riscv_target_feature), &[]), - ("zba", STABLE, &[]), - ("zbb", STABLE, &[]), - ("zbc", STABLE, &[]), - ("zbkb", STABLE, &[]), - ("zbkc", STABLE, &[]), - ("zbkx", STABLE, &[]), - ("zbs", STABLE, &[]), - ("zdinx", unstable(sym::riscv_target_feature), &["zfinx"]), - ("zfh", unstable(sym::riscv_target_feature), &["zfhmin"]), - ("zfhmin", unstable(sym::riscv_target_feature), &["f"]), - ("zfinx", unstable(sym::riscv_target_feature), &[]), - ("zhinx", unstable(sym::riscv_target_feature), &["zhinxmin"]), - ("zhinxmin", unstable(sym::riscv_target_feature), &["zfinx"]), - ("zk", STABLE, &["zkn", "zkr", "zkt"]), - ("zkn", STABLE, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), - ("zknd", STABLE, &[]), - ("zkne", STABLE, &[]), - ("zknh", STABLE, &[]), - ("zkr", STABLE, &[]), - ("zks", STABLE, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), - ("zksed", STABLE, &[]), - ("zksh", STABLE, &[]), - ("zkt", STABLE, &[]), + ("m", Stable, &[]), + ("relax", Unstable(sym::riscv_target_feature), &[]), + ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature), &[]), + ("v", Unstable(sym::riscv_target_feature), &[]), + ("zaamo", Unstable(sym::riscv_target_feature), &[]), + ("zabha", Unstable(sym::riscv_target_feature), &["zaamo"]), + ("zalrsc", Unstable(sym::riscv_target_feature), &[]), + ("zba", Stable, &[]), + ("zbb", Stable, &[]), + ("zbc", Stable, &[]), + ("zbkb", Stable, &[]), + ("zbkc", Stable, &[]), + ("zbkx", Stable, &[]), + ("zbs", Stable, &[]), + ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]), + ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]), + ("zfinx", Unstable(sym::riscv_target_feature), &[]), + ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]), + ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zk", Stable, &["zkn", "zkr", "zkt"]), + ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), + ("zknd", Stable, &[]), + ("zkne", Stable, &[]), + ("zknh", Stable, &[]), + ("zkr", Stable, &[]), + ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), + ("zksed", Stable, &[]), + ("zksh", Stable, &[]), + ("zkt", Stable, &[]), // tidy-alphabetical-end ]; -const WASM_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const 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, &[]), - ("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), &[]), + ("atomics", Unstable(sym::wasm_target_feature), &[]), + ("bulk-memory", Stable, &[]), + ("exception-handling", Unstable(sym::wasm_target_feature), &[]), + ("extended-const", Stable, &[]), + ("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, StabilityUncomputed, ImpliedFeatures)] = - &[("alu32", unstable(sym::bpf_target_feature), &[])]; +const BPF_FEATURES: &[(&str, Stability, ImpliedFeatures)] = + &[("alu32", Unstable(sym::bpf_target_feature), &[])]; -const CSKY_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const CSKY_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("10e60", unstable(sym::csky_target_feature), &["7e10"]), - ("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"]), - ("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), &[]), + ("10e60", Unstable(sym::csky_target_feature), &["7e10"]), + ("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"]), + ("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), &[]), + ("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 ]; -const LOONGARCH_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const LOONGARCH_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("d", unstable(sym::loongarch_target_feature), &["f"]), - ("f", unstable(sym::loongarch_target_feature), &[]), - ("frecipe", unstable(sym::loongarch_target_feature), &[]), - ("lasx", unstable(sym::loongarch_target_feature), &["lsx"]), - ("lbt", unstable(sym::loongarch_target_feature), &[]), - ("lsx", unstable(sym::loongarch_target_feature), &["d"]), - ("lvz", unstable(sym::loongarch_target_feature), &[]), - ("relax", unstable(sym::loongarch_target_feature), &[]), - ("ual", unstable(sym::loongarch_target_feature), &[]), + ("d", Unstable(sym::loongarch_target_feature), &["f"]), + ("f", Unstable(sym::loongarch_target_feature), &[]), + ("frecipe", Unstable(sym::loongarch_target_feature), &[]), + ("lasx", Unstable(sym::loongarch_target_feature), &["lsx"]), + ("lbt", Unstable(sym::loongarch_target_feature), &[]), + ("lsx", Unstable(sym::loongarch_target_feature), &["d"]), + ("lvz", Unstable(sym::loongarch_target_feature), &[]), + ("relax", Unstable(sym::loongarch_target_feature), &[]), + ("ual", Unstable(sym::loongarch_target_feature), &[]), // tidy-alphabetical-end ]; -const IBMZ_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("backchain", unstable(sym::s390x_target_feature), &[]), - ("vector", unstable(sym::s390x_target_feature), &[]), + ("backchain", Unstable(sym::s390x_target_feature), &[]), + ("vector", Unstable(sym::s390x_target_feature), &[]), // tidy-alphabetical-end ]; -const SPARC_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const SPARC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("leoncasa", unstable(sym::sparc_target_feature), &[]), - ("v8plus", unstable(sym::sparc_target_feature), &[]), - ("v9", unstable(sym::sparc_target_feature), &[]), + ("leoncasa", Unstable(sym::sparc_target_feature), &[]), + ("v8plus", Unstable(sym::sparc_target_feature), &[]), + ("v9", Unstable(sym::sparc_target_feature), &[]), // tidy-alphabetical-end ]; -const M68K_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const 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"]), + ("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"]), + ("isa-68881", Unstable(sym::m68k_target_feature), &[]), + ("isa-68882", Unstable(sym::m68k_target_feature), &["isa-68881"]), // tidy-alphabetical-end ]; @@ -808,7 +629,7 @@ const M68K_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ /// primitives may be documented. /// /// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator! -pub fn all_rust_features() -> impl Iterator { +pub fn all_rust_features() -> impl Iterator { std::iter::empty() .chain(ARM_FEATURES.iter()) .chain(AARCH64_FEATURES.iter()) @@ -853,10 +674,16 @@ const CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[( const LOONGARCH_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "lsx"), (256, "lasx")]; +#[derive(Copy, Clone, Debug)] +pub struct FeatureConstraints { + /// Features that must be enabled. + pub required: &'static [&'static str], + /// Features that must be disabled. + pub incompatible: &'static [&'static str], +} + impl Target { - pub fn rust_target_features( - &self, - ) -> &'static [(&'static str, StabilityUncomputed, ImpliedFeatures)] { + pub fn rust_target_features(&self) -> &'static [(&'static str, Stability, ImpliedFeatures)] { match &*self.arch { "arm" => ARM_FEATURES, "aarch64" | "arm64ec" => AARCH64_FEATURES, @@ -904,27 +731,133 @@ impl Target { } } - pub fn implied_target_features( + pub fn implied_target_features<'a>( &self, - base_features: impl Iterator, - ) -> FxHashSet { - let implied_features = self - .rust_target_features() - .iter() - .map(|(f, _, i)| (Symbol::intern(f), i)) - .collect::>(); + base_features: impl Iterator, + ) -> FxHashSet<&'a str> { + let implied_features = + self.rust_target_features().iter().map(|(f, _, i)| (f, i)).collect::>(); // implied target features have their own implied target features, so we traverse the // map until there are no more features to add let mut features = FxHashSet::default(); - let mut new_features = base_features.collect::>(); + let mut new_features = base_features.collect::>(); while let Some(new_feature) = new_features.pop() { if features.insert(new_feature) { if let Some(implied_features) = implied_features.get(&new_feature) { - new_features.extend(implied_features.iter().copied().map(Symbol::intern)) + new_features.extend(implied_features.iter().copied()) } } } features } + + /// Returns two lists of features: + /// the first list contains target features that must be enabled for ABI reasons, + /// and the second list contains target feature that must be disabled for ABI reasons. + /// + /// These features are automatically appended to whatever the target spec sats as default + /// features for the target. + /// + /// All features enabled/disabled via `-Ctarget-features` and `#[target_features]` are checked + /// against this. We also check any implied features, based on the information above. If LLVM + /// implicitly enables more implied features than we do, that could bypass this check! + pub fn abi_required_features(&self) -> FeatureConstraints { + const NOTHING: FeatureConstraints = FeatureConstraints { required: &[], incompatible: &[] }; + // Some architectures don't have a clean explicit ABI designation; instead, the ABI is + // defined by target features. When that is the case, those target features must be + // "forbidden" in the list above to ensure that there is a consistent answer to the + // questions "which ABI is used". + match &*self.arch { + "x86" => { + // We support 2 ABIs, hardfloat (default) and softfloat. + // x86 has no sane ABI indicator so we have to use the target feature. + if self.has_feature("soft-float") { + NOTHING + } else { + // Hardfloat ABI. x87 must be enabled. + FeatureConstraints { required: &["x87"], incompatible: &[] } + } + } + "x86_64" => { + // We support 2 ABIs, hardfloat (default) and softfloat. + // x86 has no sane ABI indicator so we have to use the target feature. + if self.has_feature("soft-float") { + NOTHING + } else { + // Hardfloat ABI. x87 and SSE2 must be enabled. + FeatureConstraints { required: &["x87", "sse2"], incompatible: &[] } + } + } + "arm" => { + // On ARM, ABI handling is reasonably sane; we use `llvm_floatabi` to indicate + // to LLVM which ABI we are going for. + match self.llvm_floatabi.unwrap() { + FloatAbi::Soft => { + // Nothing special required, will use soft-float ABI throughout. + // We can even allow `-soft-float` here; in fact that is useful as it lets + // people use FPU instructions with a softfloat ABI (corresponds to + // `-mfloat-abi=softfp` in GCC/clang). + NOTHING + } + FloatAbi::Hard => { + // Must have `fpregs` and must not have `soft-float`. + FeatureConstraints { required: &["fpregs"], incompatible: &["soft-float"] } + } + } + } + "aarch64" | "arm64ec" => { + // Aarch64 has no sane ABI specifier, and LLVM doesn't even have a way to force + // the use of soft-float, so all we can do here is some crude hacks. + match &*self.abi { + "softfloat" => { + // This is not fully correct, LLVM actually doesn't let us enforce the softfloat + // ABI properly... see . + // FIXME: should we forbid "neon" here? But that would be a breaking change. + NOTHING + } + _ => { + // Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled. + // These are Rust feature names and we use "neon" to control both of them. + FeatureConstraints { required: &["neon"], incompatible: &[] } + } + } + } + "riscv32" | "riscv64" => { + // RISC-V handles ABI in a very sane way, being fully explicit via `llvm_abiname` + // about what the intended ABI is. + match &*self.llvm_abiname { + "ilp32d" | "lp64d" => { + // Requires d (which implies f), incompatible with e. + FeatureConstraints { required: &["d"], incompatible: &["e"] } + } + "ilp32f" | "lp64f" => { + // Requires f, incompatible with e. + FeatureConstraints { required: &["f"], incompatible: &["e"] } + } + "ilp32" | "lp64" => { + // Requires nothing, incompatible with e. + FeatureConstraints { required: &[], incompatible: &["e"] } + } + "ilp32e" => { + // ilp32e is documented to be incompatible with features that need aligned + // load/stores > 32 bits, like `d`. (One could also just generate more + // complicated code to align the stack when needed, but the RISCV + // architecture manual just explicitly rules out this combination so we + // might as well.) + // Note that the `e` feature is not required: the ABI treats the extra + // registers as caller-save, so it is safe to use them only in some parts of + // a program while the rest doesn't know they even exist. + FeatureConstraints { required: &[], incompatible: &["d"] } + } + "lp64e" => { + // As above, `e` is not required. + NOTHING + } + _ => unreachable!(), + } + } + _ => NOTHING, + } + } } 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 f9a304083260..7ba87e180d0b 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 @@ -114,7 +114,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // // We rely on a few heuristics to identify cases where this root // obligation is more important than the leaf obligation: - let (main_trait_predicate, o) = if let ty::PredicateKind::Clause( + let (main_trait_predicate, main_obligation) = if let ty::PredicateKind::Clause( ty::ClauseKind::Trait(root_pred) ) = root_obligation.predicate.kind().skip_binder() && !leaf_trait_predicate.self_ty().skip_binder().has_escaping_bound_vars() @@ -199,7 +199,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { notes, parent_label, append_const_msg, - } = self.on_unimplemented_note(main_trait_predicate, o, &mut long_ty_file); + } = self.on_unimplemented_note(main_trait_predicate, main_obligation, &mut long_ty_file); let have_alt_message = message.is_some() || label.is_some(); let is_try_conversion = self.is_try_conversion(span, main_trait_ref.def_id()); @@ -538,23 +538,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => { - // FIXME(const_trait_impl): We should recompute the predicate with `~const` - // if it's `const`, and if it holds, explain that this bound only - // *conditionally* holds. If that fails, we should also do selection - // to drill this down to an impl or built-in source, so we can - // point at it and explain that while the trait *is* implemented, - // that implementation is not const. - let err_msg = self.get_standard_error_message( - bound_predicate.rebind(ty::TraitPredicate { - trait_ref: predicate.trait_ref, - polarity: ty::PredicatePolarity::Positive, - }), - None, - Some(predicate.constness), - None, - String::new(), - ); - struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg) + self.report_host_effect_error(bound_predicate.rebind(predicate), obligation.param_env, span) } ty::PredicateKind::Subtype(predicate) => { @@ -753,7 +737,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { applied_do_not_recommend = true; } } - if let Some((parent_cause, _parent_pred)) = base_cause.parent() { + if let Some(parent_cause) = base_cause.parent() { base_cause = parent_cause.clone(); } else { break; @@ -763,6 +747,41 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { applied_do_not_recommend } + fn report_host_effect_error( + &self, + predicate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + ) -> Diag<'a> { + // FIXME(const_trait_impl): We should recompute the predicate with `~const` + // if it's `const`, and if it holds, explain that this bound only + // *conditionally* holds. If that fails, we should also do selection + // to drill this down to an impl or built-in source, so we can + // point at it and explain that while the trait *is* implemented, + // that implementation is not const. + let trait_ref = predicate.map_bound(|predicate| ty::TraitPredicate { + trait_ref: predicate.trait_ref, + polarity: ty::PredicatePolarity::Positive, + }); + let err_msg = self.get_standard_error_message( + trait_ref, + None, + Some(predicate.constness()), + None, + String::new(), + ); + let mut diag = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg); + if !self.predicate_may_hold(&Obligation::new( + self.tcx, + ObligationCause::dummy(), + param_env, + trait_ref, + )) { + diag.downgrade_to_delayed_bug(); + } + diag + } + fn emit_specialized_closure_kind_error( &self, obligation: &PredicateObligation<'tcx>, @@ -778,7 +797,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_ref.skip_binder().args.type_at(1).to_opt_closure_kind() && !found_kind.extends(expected_kind) { - if let Some((_, Some(parent))) = obligation.cause.code().parent() { + if let Some((_, Some(parent))) = obligation.cause.code().parent_with_predicate() { // If we have a derived obligation, then the parent will be a `AsyncFn*` goal. trait_ref = parent.to_poly_trait_ref(); } else if let &ObligationCauseCode::FunctionArg { arg_hir_id, .. } = @@ -926,7 +945,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let Some(typeck) = &self.typeck_results else { return false; }; - let Some((ObligationCauseCode::QuestionMark, Some(y))) = obligation.cause.code().parent() + let Some((ObligationCauseCode::QuestionMark, Some(y))) = + obligation.cause.code().parent_with_predicate() else { return false; }; @@ -1179,7 +1199,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut code = obligation.cause.code(); let mut pred = obligation.predicate.as_trait_clause(); - while let Some((next_code, next_pred)) = code.parent() { + while let Some((next_code, next_pred)) = code.parent_with_predicate() { if let Some(pred) = pred { self.enter_forall(pred, |pred| { diag.note(format!( @@ -2095,7 +2115,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut code = obligation.cause.code(); let mut trait_pred = trait_predicate; let mut peeled = false; - while let Some((parent_code, parent_trait_pred)) = code.parent() { + while let Some((parent_code, parent_trait_pred)) = code.parent_with_predicate() { code = parent_code; if let Some(parent_trait_pred) = parent_trait_pred { trait_pred = parent_trait_pred; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 007a220ae697..9d85ca1dd4dd 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -464,7 +464,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Get the root obligation, since the leaf obligation we have may be unhelpful (#87437) let mut real_trait_pred = trait_pred; - while let Some((parent_code, parent_trait_pred)) = code.parent() { + while let Some((parent_code, parent_trait_pred)) = code.parent_with_predicate() { code = parent_code; if let Some(parent_trait_pred) = parent_trait_pred { real_trait_pred = parent_trait_pred; @@ -1447,7 +1447,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut span = obligation.cause.span; let mut trait_pred = trait_pred; let mut code = obligation.cause.code(); - while let Some((c, Some(parent_trait_pred))) = code.parent() { + while let Some((c, Some(parent_trait_pred))) = code.parent_with_predicate() { // We want the root obligation, in order to detect properly handle // `for _ in &mut &mut vec![] {}`. code = c; @@ -3470,6 +3470,59 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) }); } + ObligationCauseCode::ImplDerivedHost(ref data) => { + let self_ty = + self.resolve_vars_if_possible(data.derived.parent_host_pred.self_ty()); + let msg = format!( + "required for `{self_ty}` to implement `{} {}`", + data.derived.parent_host_pred.skip_binder().constness, + data.derived + .parent_host_pred + .map_bound(|pred| pred.trait_ref) + .print_only_trait_path(), + ); + match tcx.hir().get_if_local(data.impl_def_id) { + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), + .. + })) => { + let mut spans = vec![self_ty.span]; + spans.extend(of_trait.as_ref().map(|t| t.path.span)); + let mut spans: MultiSpan = spans.into(); + spans.push_span_label(data.span, "unsatisfied trait bound introduced here"); + err.span_note(spans, msg); + } + _ => { + err.note(msg); + } + } + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + body_id, + err, + data.derived.parent_host_pred, + param_env, + &data.derived.parent_code, + obligated_types, + seen_requirements, + long_ty_file, + ) + }); + } + ObligationCauseCode::BuiltinDerivedHost(ref data) => { + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + body_id, + err, + data.parent_host_pred, + param_env, + &data.parent_code, + obligated_types, + seen_requirements, + long_ty_file, + ) + }); + } ObligationCauseCode::WellFormedDerived(ref data) => { let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); let parent_predicate = parent_trait_ref; diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 2b2623a050ec..7db0f2bb5a7c 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -10,9 +10,9 @@ use rustc_infer::traits::{ self, FromSolverError, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, PredicateObligations, SelectionError, TraitEngine, }; -use rustc_middle::bug; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _}; use tracing::{instrument, trace}; @@ -258,6 +258,23 @@ fn fulfillment_error_for_no_solution<'tcx>( MismatchedProjectionTypes { err: TypeError::Mismatch }, ) } + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => { + let ct_ty = match ct.kind() { + ty::ConstKind::Unevaluated(uv) => { + infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args) + } + ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env), + _ => span_bug!( + obligation.cause.span, + "ConstArgHasWrongType failed but we don't know how to compute type" + ), + }; + FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType { + ct, + ct_ty, + expected_ty, + }) + } ty::PredicateKind::NormalizesTo(..) => { FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) } @@ -413,6 +430,7 @@ impl<'tcx> BestObligation<'tcx> { matches!( nested_goal.source(), GoalSource::ImplWhereBound + | GoalSource::AliasBoundConstCondition | GoalSource::InstantiateHigherRanked | GoalSource::AliasWellFormed ) && match self.consider_ambiguities { @@ -474,8 +492,11 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { // for normalizes-to. let pred_kind = goal.goal().predicate.kind(); let child_mode = match pred_kind.skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(parent_trait_pred)) => { - ChildMode::Trait(pred_kind.rebind(parent_trait_pred)) + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { + ChildMode::Trait(pred_kind.rebind(pred)) + } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => { + ChildMode::Host(pred_kind.rebind(pred)) } ty::PredicateKind::NormalizesTo(normalizes_to) if matches!( @@ -504,7 +525,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { let obligation; match (child_mode, nested_goal.source()) { - (ChildMode::Trait(_), GoalSource::Misc) => { + (ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => { continue; } (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => { @@ -517,11 +538,25 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { )); impl_where_bound_count += 1; } + ( + ChildMode::Host(parent_host_pred), + GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition, + ) => { + obligation = make_obligation(derive_host_cause( + tcx, + candidate.kind(), + self.obligation.cause.clone(), + impl_where_bound_count, + parent_host_pred, + )); + impl_where_bound_count += 1; + } // Skip over a higher-ranked predicate. (_, GoalSource::InstantiateHigherRanked) => { obligation = self.obligation.clone(); } - (ChildMode::PassThrough, _) | (_, GoalSource::AliasWellFormed) => { + (ChildMode::PassThrough, _) + | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => { obligation = make_obligation(self.obligation.cause.clone()); } } @@ -575,6 +610,10 @@ enum ChildMode<'tcx> { // and skip all `GoalSource::Misc`, which represent useless obligations // such as alias-eq which may not hold. Trait(ty::PolyTraitPredicate<'tcx>), + // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, + // and skip all `GoalSource::Misc`, which represent useless obligations + // such as alias-eq which may not hold. + Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>), // Skip trying to derive an `ObligationCause` from this obligation, and // report *all* sub-obligations as if they came directly from the parent // obligation. @@ -616,3 +655,52 @@ fn derive_cause<'tcx>( }; cause } + +fn derive_host_cause<'tcx>( + tcx: TyCtxt<'tcx>, + candidate_kind: inspect::ProbeKind>, + mut cause: ObligationCause<'tcx>, + idx: usize, + parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, +) -> ObligationCause<'tcx> { + match candidate_kind { + inspect::ProbeKind::TraitCandidate { + source: CandidateSource::Impl(impl_def_id), + result: _, + } => { + if let Some((_, span)) = tcx + .predicates_of(impl_def_id) + .instantiate_identity(tcx) + .into_iter() + .chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map( + |(trait_ref, span)| { + ( + trait_ref.to_host_effect_clause( + tcx, + parent_host_pred.skip_binder().constness, + ), + span, + ) + }, + )) + .nth(idx) + { + cause = + cause.derived_host_cause(parent_host_pred, |derived| { + ObligationCauseCode::ImplDerivedHost(Box::new( + traits::ImplDerivedHostCause { derived, impl_def_id, span }, + )) + }) + } + } + inspect::ProbeKind::TraitCandidate { + source: CandidateSource::BuiltinImpl(..), + result: _, + } => { + cause = + cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost); + } + _ => {} + }; + cause +} diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 91484ef99dbb..0ac24eb54e74 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -1,6 +1,8 @@ use rustc_hir as hir; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes}; -use rustc_infer::traits::{ImplSource, Obligation, PredicateObligation}; +use rustc_infer::traits::{ + ImplDerivedHostCause, ImplSource, Obligation, ObligationCauseCode, PredicateObligation, +}; use rustc_middle::span_bug; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{self, TypingMode}; @@ -248,9 +250,22 @@ fn evaluate_host_effect_from_selection_candiate<'tcx>( tcx.const_conditions(impl_.impl_def_id) .instantiate(tcx, impl_.args) .into_iter() - .map(|(trait_ref, _)| { - obligation.with( + .map(|(trait_ref, span)| { + Obligation::new( tcx, + obligation.cause.clone().derived_host_cause( + ty::Binder::dummy(obligation.predicate), + |derived| { + ObligationCauseCode::ImplDerivedHost(Box::new( + ImplDerivedHostCause { + derived, + impl_def_id: impl_.impl_def_id, + span, + }, + )) + }, + ), + obligation.param_env, trait_ref .to_host_effect_clause(tcx, obligation.predicate.constness), ) diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 47447af22158..cb59bc608c20 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -89,6 +89,7 @@ impl_binder_encode_decode! { ty::ExistentialPredicate, ty::TraitRef, ty::ExistentialTraitRef, + ty::HostEffectPredicate, } impl Binder diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 8fe512026e5d..1ae904d50e06 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -68,6 +68,10 @@ pub enum GoalSource { /// FIXME(-Znext-solver=coinductive): Explain how and why this /// changes whether cycles are coinductive. ImplWhereBound, + /// Const conditions that need to hold for `~const` alias bounds to hold. + /// + /// FIXME(-Znext-solver=coinductive): Are these even coinductive? + AliasBoundConstCondition, /// Instantiating a higher-ranked goal and re-proving it. InstantiateHigherRanked, /// Predicate required for an alias projection to be well-formed. diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 23d060d2158c..0c9535dfaa62 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -62,10 +62,10 @@ use crate::alloc::Allocator; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::collections::TryReserveError; -use crate::str::{self, Chars, Utf8Error, from_utf8_unchecked_mut}; +use crate::str::{self, CharIndices, Chars, Utf8Error, from_utf8_unchecked_mut}; #[cfg(not(no_global_oom_handling))] use crate::str::{FromStr, from_boxed_utf8_unchecked}; -use crate::vec::Vec; +use crate::vec::{self, Vec}; /// A UTF-8–encoded, growable string. /// @@ -1952,6 +1952,61 @@ impl String { Drain { start, end, iter: chars_iter, string: self_ptr } } + /// Converts a `String` into an iterator over the [`char`]s of the string. + /// + /// As a string consists of valid UTF-8, we can iterate through a string + /// by [`char`]. This method returns such an iterator. + /// + /// It's important to remember that [`char`] represents a Unicode Scalar + /// Value, and might not match your idea of what a 'character' is. Iteration + /// over grapheme clusters may be what you actually want. That functionality + /// is not provided by Rust's standard library, check crates.io instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(string_into_chars)] + /// + /// let word = String::from("goodbye"); + /// + /// let mut chars = word.into_chars(); + /// + /// assert_eq!(Some('g'), chars.next()); + /// assert_eq!(Some('o'), chars.next()); + /// assert_eq!(Some('o'), chars.next()); + /// assert_eq!(Some('d'), chars.next()); + /// assert_eq!(Some('b'), chars.next()); + /// assert_eq!(Some('y'), chars.next()); + /// assert_eq!(Some('e'), chars.next()); + /// + /// assert_eq!(None, chars.next()); + /// ``` + /// + /// Remember, [`char`]s might not match your intuition about characters: + /// + /// ``` + /// #![feature(string_into_chars)] + /// + /// let y = String::from("y̆"); + /// + /// let mut chars = y.into_chars(); + /// + /// assert_eq!(Some('y'), chars.next()); // not 'y̆' + /// assert_eq!(Some('\u{0306}'), chars.next()); + /// + /// assert_eq!(None, chars.next()); + /// ``` + /// + /// [`char`]: prim@char + #[inline] + #[must_use = "`self` will be dropped if the result is not used"] + #[unstable(feature = "string_into_chars", issue = "133125")] + pub fn into_chars(self) -> IntoChars { + IntoChars { bytes: self.into_bytes().into_iter() } + } + /// Removes the specified range in the string, /// and replaces it with the given string. /// The given string doesn't need to be the same length as the range. @@ -3090,6 +3145,134 @@ impl fmt::Write for String { } } +/// An iterator over the [`char`]s of a string. +/// +/// This struct is created by the [`into_chars`] method on [`String`]. +/// See its documentation for more. +/// +/// [`char`]: prim@char +/// [`into_chars`]: String::into_chars +#[cfg_attr(not(no_global_oom_handling), derive(Clone))] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[unstable(feature = "string_into_chars", issue = "133125")] +pub struct IntoChars { + bytes: vec::IntoIter, +} + +#[unstable(feature = "string_into_chars", issue = "133125")] +impl fmt::Debug for IntoChars { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IntoChars").field(&self.as_str()).finish() + } +} + +impl IntoChars { + /// Views the underlying data as a subslice of the original data. + /// + /// # Examples + /// + /// ``` + /// #![feature(string_into_chars)] + /// + /// let mut chars = String::from("abc").into_chars(); + /// + /// assert_eq!(chars.as_str(), "abc"); + /// chars.next(); + /// assert_eq!(chars.as_str(), "bc"); + /// chars.next(); + /// chars.next(); + /// assert_eq!(chars.as_str(), ""); + /// ``` + #[unstable(feature = "string_into_chars", issue = "133125")] + #[must_use] + #[inline] + pub fn as_str(&self) -> &str { + // SAFETY: `bytes` is a valid UTF-8 string. + unsafe { str::from_utf8_unchecked(self.bytes.as_slice()) } + } + + /// Consumes the `IntoChars`, returning the remaining string. + /// + /// # Examples + /// + /// ``` + /// #![feature(string_into_chars)] + /// + /// let chars = String::from("abc").into_chars(); + /// assert_eq!(chars.into_string(), "abc"); + /// + /// let mut chars = String::from("def").into_chars(); + /// chars.next(); + /// assert_eq!(chars.into_string(), "ef"); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "string_into_chars", issue = "133125")] + #[inline] + pub fn into_string(self) -> String { + // Safety: `bytes` are kept in UTF-8 form, only removing whole `char`s at a time. + unsafe { String::from_utf8_unchecked(self.bytes.collect()) } + } + + #[inline] + fn iter(&self) -> CharIndices<'_> { + self.as_str().char_indices() + } +} + +#[unstable(feature = "string_into_chars", issue = "133125")] +impl Iterator for IntoChars { + type Item = char; + + #[inline] + fn next(&mut self) -> Option { + let mut iter = self.iter(); + match iter.next() { + None => None, + Some((_, ch)) => { + let offset = iter.offset(); + // `offset` is a valid index. + let _ = self.bytes.advance_by(offset); + Some(ch) + } + } + } + + #[inline] + fn count(self) -> usize { + self.iter().count() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter().size_hint() + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } +} + +#[unstable(feature = "string_into_chars", issue = "133125")] +impl DoubleEndedIterator for IntoChars { + #[inline] + fn next_back(&mut self) -> Option { + let len = self.as_str().len(); + let mut iter = self.iter(); + match iter.next_back() { + None => None, + Some((idx, ch)) => { + // `idx` is a valid index. + let _ = self.bytes.advance_back_by(len - idx); + Some(ch) + } + } + } +} + +#[unstable(feature = "string_into_chars", issue = "133125")] +impl FusedIterator for IntoChars {} + /// A draining iterator for `String`. /// /// This struct is created by the [`drain`] method on [`String`]. See its diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index c2c78dd9c67e..a033b8bd3051 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -596,7 +596,7 @@ impl<'a> Arguments<'a> { /// When using the format_args!() macro, this function is used to generate the /// Arguments structure. #[inline] - pub fn new_v1( + pub const fn new_v1( pieces: &'a [&'static str; P], args: &'a [rt::Argument<'a>; A], ) -> Arguments<'a> { @@ -612,7 +612,7 @@ impl<'a> Arguments<'a> { /// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`. /// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`. #[inline] - pub fn new_v1_formatted( + pub const fn new_v1_formatted( pieces: &'a [&'static str], args: &'a [rt::Argument<'a>], fmt: &'a [rt::Placeholder], diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs index 94341a4da66c..85d089a07908 100644 --- a/library/core/src/fmt/rt.rs +++ b/library/core/src/fmt/rt.rs @@ -96,12 +96,12 @@ pub struct Argument<'a> { #[rustc_diagnostic_item = "ArgumentMethods"] impl Argument<'_> { #[inline] - fn new<'a, T>(x: &'a T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'a> { + const fn new<'a, T>(x: &'a T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'a> { Argument { // INVARIANT: this creates an `ArgumentType<'a>` from a `&'a T` and // a `fn(&T, ...)`, so the invariant is maintained. ty: ArgumentType::Placeholder { - value: NonNull::from(x).cast(), + value: NonNull::from_ref(x).cast(), // SAFETY: function pointers always have the same layout. formatter: unsafe { mem::transmute(f) }, _lifetime: PhantomData, @@ -150,7 +150,7 @@ impl Argument<'_> { Self::new(x, UpperExp::fmt) } #[inline] - pub fn from_usize(x: &usize) -> Argument<'_> { + pub const fn from_usize(x: &usize) -> Argument<'_> { Argument { ty: ArgumentType::Count(*x) } } @@ -181,7 +181,7 @@ impl Argument<'_> { } #[inline] - pub(super) fn as_usize(&self) -> Option { + pub(super) const fn as_usize(&self) -> Option { match self.ty { ArgumentType::Count(count) => Some(count), ArgumentType::Placeholder { .. } => None, @@ -199,7 +199,7 @@ impl Argument<'_> { /// println!("{f}"); /// ``` #[inline] - pub fn none() -> [Self; 0] { + pub const fn none() -> [Self; 0] { [] } } @@ -216,7 +216,7 @@ impl UnsafeArg { /// See documentation where `UnsafeArg` is required to know when it is safe to /// create and use `UnsafeArg`. #[inline] - pub unsafe fn new() -> Self { + pub const unsafe fn new() -> Self { Self { _private: () } } } diff --git a/library/core/src/iter/sources/from_fn.rs b/library/core/src/iter/sources/from_fn.rs index 3cd3830471cf..5f3d404d7dca 100644 --- a/library/core/src/iter/sources/from_fn.rs +++ b/library/core/src/iter/sources/from_fn.rs @@ -3,6 +3,8 @@ use crate::fmt; /// Creates a new iterator where each iteration calls the provided closure /// `F: FnMut() -> Option`. /// +/// The iterator will yield the `T`s returned from the closure. +/// /// This allows creating a custom iterator with any behavior /// without using the more verbose syntax of creating a dedicated type /// and implementing the [`Iterator`] trait for it. diff --git a/library/core/src/iter/sources/successors.rs b/library/core/src/iter/sources/successors.rs index 36bc4035039e..e14c9235e556 100644 --- a/library/core/src/iter/sources/successors.rs +++ b/library/core/src/iter/sources/successors.rs @@ -5,6 +5,7 @@ use crate::iter::FusedIterator; /// /// The iterator starts with the given first item (if any) /// and calls the given `FnMut(&T) -> Option` closure to compute each item’s successor. +/// The iterator will yield the `T`s returned from the closure. /// /// ``` /// use std::iter::successors; diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 073cca7daef0..f800e0d391c9 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -987,8 +987,9 @@ impl [T] { /// assert!(v == [3, 2, 1]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_reverse", issue = "135120")] #[inline] - pub fn reverse(&mut self) { + pub const fn reverse(&mut self) { let half_len = self.len() / 2; let Range { start, end } = self.as_mut_ptr_range(); @@ -1011,7 +1012,7 @@ impl [T] { revswap(front_half, back_half, half_len); #[inline] - fn revswap(a: &mut [T], b: &mut [T], n: usize) { + const fn revswap(a: &mut [T], b: &mut [T], n: usize) { debug_assert!(a.len() == n); debug_assert!(b.len() == n); @@ -1019,7 +1020,8 @@ impl [T] { // this check tells LLVM that the indexing below is // in-bounds. Then after inlining -- once the actual // lengths of the slices are known -- it's removed. - let (a, b) = (&mut a[..n], &mut b[..n]); + let (a, _) = a.split_at_mut(n); + let (b, _) = b.split_at_mut(n); let mut i = 0; while i < n { diff --git a/library/panic_unwind/Cargo.toml b/library/panic_unwind/Cargo.toml index 252f118fecfb..c2abb79192e9 100644 --- a/library/panic_unwind/Cargo.toml +++ b/library/panic_unwind/Cargo.toml @@ -23,4 +23,4 @@ libc = { version = "0.2", default-features = false } [lints.rust.unexpected_cfgs] level = "warn" -check-cfg = [] +check-cfg = ['cfg(emscripten_wasm_eh)'] diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 8c28bb5c5b03..dc78be76cb4d 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -25,13 +25,14 @@ // `real_imp` is unused with Miri, so silence warnings. #![cfg_attr(miri, allow(dead_code))] #![allow(internal_features)] +#![cfg_attr(not(bootstrap), feature(cfg_emscripten_wasm_eh))] use alloc::boxed::Box; use core::any::Any; use core::panic::PanicPayload; cfg_if::cfg_if! { - if #[cfg(target_os = "emscripten")] { + if #[cfg(all(target_os = "emscripten", not(emscripten_wasm_eh)))] { #[path = "emcc.rs"] mod imp; } else if #[cfg(target_os = "hermit")] { diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index e93e915159e4..4f37e18a8cd7 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -227,6 +227,7 @@ impl f128 { /// ``` #[inline] #[rustc_allow_incoherent_impl] + #[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn mul_add(self, a: f128, b: f128) -> f128 { @@ -384,6 +385,7 @@ impl f128 { /// # } /// ``` #[inline] + #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index 5b7fcaa28e06..42cd6e3fe2a5 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -228,6 +228,7 @@ impl f16 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] + #[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn mul_add(self, a: f16, b: f16) -> f16 { unsafe { intrinsics::fmaf16(self, a, b) } @@ -384,6 +385,7 @@ impl f16 { /// # } /// ``` #[inline] + #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 7cb285bbff5f..438d77b1626b 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -210,6 +210,7 @@ impl f32 { /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); /// ``` #[rustc_allow_incoherent_impl] + #[doc(alias = "fmaf", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -360,6 +361,7 @@ impl f32 { /// assert!(negative.sqrt().is_nan()); /// assert!(negative_zero.sqrt() == negative_zero); /// ``` + #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 47163c272de3..9bb4bfbab2a0 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -210,6 +210,7 @@ impl f64 { /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); /// ``` #[rustc_allow_incoherent_impl] + #[doc(alias = "fma", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -360,6 +361,7 @@ impl f64 { /// assert!(negative.sqrt().is_nan()); /// assert!(negative_zero.sqrt() == negative_zero); /// ``` + #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index fff140f1564f..7fb57d410431 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -550,7 +550,7 @@ impl OsString { OsStr::from_inner_mut(self.inner.leak()) } - /// Truncate the the `OsString` to the specified length. + /// Truncate the `OsString` to the specified length. /// /// # Panics /// Panics if `len` does not lie on a valid `OsStr` boundary diff --git a/library/std/src/pipe.rs b/library/std/src/pipe.rs index 06f3fd9fdffe..913c22588a76 100644 --- a/library/std/src/pipe.rs +++ b/library/std/src/pipe.rs @@ -97,7 +97,7 @@ impl PipeReader { /// let mut jobs = vec![]; /// let (reader, mut writer) = std::pipe::pipe()?; /// - /// // Write NUM_SLOT characters the the pipe. + /// // Write NUM_SLOT characters the pipe. /// writer.write_all(&[b'|'; NUM_SLOT as usize])?; /// /// // Spawn several processes that read a character from the pipe, do some work, then diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 929d2b57afe5..e0dd2e14817a 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -868,13 +868,17 @@ impl Command { /// /// # Examples /// + /// Prevent any inherited `GIT_DIR` variable from changing the target of the `git` command, + /// while allowing all other variables, like `GIT_AUTHOR_NAME`. + /// /// ```no_run /// use std::process::Command; /// - /// Command::new("ls") - /// .env_remove("PATH") - /// .spawn() - /// .expect("ls command failed to start"); + /// Command::new("git") + /// .arg("commit") + /// .env_remove("GIT_DIR") + /// .spawn()?; + /// # std::io::Result::Ok(()) /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn env_remove>(&mut self, key: K) -> &mut Command { @@ -896,13 +900,17 @@ impl Command { /// /// # Examples /// + /// The behavior of `sort` is affected by `LANG` and `LC_*` environment variables. + /// Clearing the environment makes `sort`'s behavior independent of the parent processes' language. + /// /// ```no_run /// use std::process::Command; /// - /// Command::new("ls") + /// Command::new("sort") + /// .arg("file.txt") /// .env_clear() - /// .spawn() - /// .expect("ls command failed to start"); + /// .spawn()?; + /// # std::io::Result::Ok(()) /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn env_clear(&mut self) -> &mut Command { @@ -1283,13 +1291,13 @@ impl fmt::Debug for Output { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let stdout_utf8 = str::from_utf8(&self.stdout); let stdout_debug: &dyn fmt::Debug = match stdout_utf8 { - Ok(ref str) => str, + Ok(ref s) => s, Err(_) => &self.stdout, }; let stderr_utf8 = str::from_utf8(&self.stderr); let stderr_debug: &dyn fmt::Debug = match stderr_utf8 { - Ok(ref str) => str, + Ok(ref s) => s, Err(_) => &self.stderr, }; diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index e28c2090afed..01ef71a187fe 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -534,7 +534,7 @@ impl Mutex { /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error containing the the underlying data + /// this call will return an error containing the underlying data /// instead. /// /// # Examples diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs index 2ca20a21dfe5..9332c9b49ffb 100644 --- a/library/std/src/sys/pal/windows/process.rs +++ b/library/std/src/sys/pal/windows/process.rs @@ -142,11 +142,11 @@ impl AsRef for EnvKey { } } -pub(crate) fn ensure_no_nuls>(str: T) -> io::Result { - if str.as_ref().encode_wide().any(|b| b == 0) { +pub(crate) fn ensure_no_nuls>(s: T) -> io::Result { + if s.as_ref().encode_wide().any(|b| b == 0) { Err(io::const_error!(ErrorKind::InvalidInput, "nul byte found in provided data")) } else { - Ok(str) + Ok(s) } } diff --git a/library/std/src/sys/sync/condvar/no_threads.rs b/library/std/src/sys/sync/condvar/no_threads.rs index 88ce39305e1a..18d97d4b17ab 100644 --- a/library/std/src/sys/sync/condvar/no_threads.rs +++ b/library/std/src/sys/sync/condvar/no_threads.rs @@ -1,4 +1,5 @@ use crate::sys::sync::Mutex; +use crate::thread::sleep; use crate::time::Duration; pub struct Condvar {} @@ -19,7 +20,8 @@ impl Condvar { panic!("condvar wait not supported") } - pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { - panic!("condvar wait not supported"); + pub unsafe fn wait_timeout(&self, _mutex: &Mutex, dur: Duration) -> bool { + sleep(dur); + false } } diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index 666942bb8a10..6c60d901ee90 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -204,8 +204,8 @@ impl Wtf8Buf { /// /// Since WTF-8 is a superset of UTF-8, this always succeeds. #[inline] - pub fn from_str(str: &str) -> Wtf8Buf { - Wtf8Buf { bytes: <[_]>::to_vec(str.as_bytes()), is_known_utf8: true } + pub fn from_str(s: &str) -> Wtf8Buf { + Wtf8Buf { bytes: <[_]>::to_vec(s.as_bytes()), is_known_utf8: true } } pub fn clear(&mut self) { diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index e13c9a06c05d..66e8d1a3ffe5 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -37,4 +37,4 @@ system-llvm-libunwind = [] [lints.rust.unexpected_cfgs] level = "warn" -check-cfg = [] +check-cfg = ['cfg(emscripten_wasm_eh)'] diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 7af1882ab73a..e4ba2bc1ed87 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -4,10 +4,11 @@ #![feature(staged_api)] #![cfg_attr(not(target_env = "msvc"), feature(libc))] #![cfg_attr( - all(target_family = "wasm", not(target_os = "emscripten")), + all(target_family = "wasm", any(not(target_os = "emscripten"), emscripten_wasm_eh)), feature(simd_wasm64, wasm_exception_handling_intrinsics) )] #![allow(internal_features)] +#![cfg_attr(not(bootstrap), feature(cfg_emscripten_wasm_eh))] // Force libc to be included even if unused. This is required by many platforms. #[cfg(not(all(windows, target_env = "msvc")))] diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 148b96181d1d..b4c56df6ea6d 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1207,6 +1207,15 @@ pub fn rustc_cargo_env( rustc_llvm_env(builder, cargo, target) } } + + // Build jemalloc on AArch64 with support for page sizes up to 64K + // See: https://github.com/rust-lang/rust/pull/135081 + if builder.config.jemalloc + && target.starts_with("aarch64") + && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() + { + cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16"); + } } /// Pass down configuration from the LLVM build into the build of diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 64bcb0b85f4f..dc6dbbac9d25 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -27,7 +27,7 @@ use crate::utils::helpers::{ linker_args, linker_flags, t, target_supports_cranelift_backend, up_to_date, }; use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; -use crate::{CLang, DocTests, GitRepo, Mode, envify}; +use crate::{CLang, DocTests, GitRepo, Mode, PathSet, envify}; const ADB_TEST_DIR: &str = "/data/local/tmp/work"; @@ -1185,53 +1185,6 @@ macro_rules! test { }; } -/// Declares an alias for running the [`Coverage`] tests in only one mode. -/// Adapted from [`test`]. -macro_rules! coverage_test_alias { - ( - $( #[$attr:meta] )* // allow docstrings and attributes - $name:ident { - alias_and_mode: $alias_and_mode:expr, // &'static str - default: $default:expr, // bool - only_hosts: $only_hosts:expr // bool - $( , )? // optional trailing comma - } - ) => { - $( #[$attr] )* - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $name { - pub compiler: Compiler, - pub target: TargetSelection, - } - - impl $name { - const MODE: &'static str = $alias_and_mode; - } - - impl Step for $name { - type Output = (); - const DEFAULT: bool = $default; - const ONLY_HOSTS: bool = $only_hosts; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - // Register the mode name as a command-line alias. - // This allows `x test coverage-map` and `x test coverage-run`. - run.alias($alias_and_mode) - } - - fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - - run.builder.ensure($name { compiler, target: run.target }); - } - - fn run(self, builder: &Builder<'_>) { - Coverage::run_coverage_tests(builder, self.compiler, self.target, Self::MODE); - } - } - }; -} - #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] pub struct RunMakeSupport { pub compiler: Compiler, @@ -1473,44 +1426,88 @@ impl Step for RunMake { test!(Assembly { path: "tests/assembly", mode: "assembly", suite: "assembly", default: true }); -/// Coverage tests are a bit more complicated than other test suites, because -/// we want to run the same set of test files in multiple different modes, -/// in a way that's convenient and flexible when invoked manually. -/// -/// This combined step runs the specified tests (or all of `tests/coverage`) -/// in both "coverage-map" and "coverage-run" modes. -/// -/// Used by: -/// - `x test coverage` -/// - `x test tests/coverage` -/// - `x test tests/coverage/trivial.rs` (etc) -/// -/// (Each individual mode also has its own step that will run the tests in -/// just that mode.) -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +/// Runs the coverage test suite at `tests/coverage` in some or all of the +/// coverage test modes. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Coverage { pub compiler: Compiler, pub target: TargetSelection, + pub mode: &'static str, } impl Coverage { const PATH: &'static str = "tests/coverage"; const SUITE: &'static str = "coverage"; + const ALL_MODES: &[&str] = &["coverage-map", "coverage-run"]; +} - /// Runs the coverage test suite (or a user-specified subset) in one mode. - /// - /// This same function is used by the multi-mode step ([`Coverage`]) and by - /// the single-mode steps ([`CoverageMap`] and [`CoverageRun`]), to help - /// ensure that they all behave consistently with each other, regardless of - /// how the coverage tests have been invoked. - fn run_coverage_tests( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, - mode: &'static str, - ) { - // Like many other test steps, we delegate to a `Compiletest` step to - // actually run the tests. (See `test_definitions!`.) +impl Step for Coverage { + type Output = (); + const DEFAULT: bool = true; + /// Compiletest will automatically skip the "coverage-run" tests if necessary. + const ONLY_HOSTS: bool = false; + + fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> { + // Support various invocation styles, including: + // - `./x test coverage` + // - `./x test tests/coverage/trivial.rs` + // - `./x test coverage-map` + // - `./x test coverage-run -- tests/coverage/trivial.rs` + run = run.suite_path(Self::PATH); + for mode in Self::ALL_MODES { + run = run.alias(mode); + } + run + } + + fn make_run(run: RunConfig<'_>) { + let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); + let target = run.target; + + // List of (coverage) test modes that the coverage test suite will be + // run in. It's OK for this to contain duplicates, because the call to + // `Builder::ensure` below will take care of deduplication. + let mut modes = vec![]; + + // From the pathsets that were selected on the command-line (or by default), + // determine which modes to run in. + for path in &run.paths { + match path { + PathSet::Set(_) => { + for mode in Self::ALL_MODES { + if path.assert_single_path().path == Path::new(mode) { + modes.push(mode); + break; + } + } + } + PathSet::Suite(_) => { + modes.extend(Self::ALL_MODES); + break; + } + } + } + + // Skip any modes that were explicitly skipped/excluded on the command-line. + // FIXME(Zalathar): Integrate this into central skip handling somehow? + modes.retain(|mode| !run.builder.config.skip.iter().any(|skip| skip == Path::new(mode))); + + // FIXME(Zalathar): Make these commands skip all coverage tests, as expected: + // - `./x test --skip=tests` + // - `./x test --skip=tests/coverage` + // - `./x test --skip=coverage` + // Skip handling currently doesn't have a way to know that skipping the coverage + // suite should also skip the `coverage-map` and `coverage-run` aliases. + + for mode in modes { + run.builder.ensure(Coverage { compiler, target, mode }); + } + } + + fn run(self, builder: &Builder<'_>) { + let Self { compiler, target, mode } = self; + // Like other compiletest suite test steps, delegate to an internal + // compiletest task to actually run the tests. builder.ensure(Compiletest { compiler, target, @@ -1522,53 +1519,6 @@ impl Coverage { } } -impl Step for Coverage { - type Output = (); - /// We rely on the individual CoverageMap/CoverageRun steps to run themselves. - const DEFAULT: bool = false; - /// When manually invoked, try to run as much as possible. - /// Compiletest will automatically skip the "coverage-run" tests if necessary. - const ONLY_HOSTS: bool = false; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - // Take responsibility for command-line paths within `tests/coverage`. - run.suite_path(Self::PATH) - } - - fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - - run.builder.ensure(Coverage { compiler, target: run.target }); - } - - fn run(self, builder: &Builder<'_>) { - // Run the specified coverage tests (possibly all of them) in both modes. - Self::run_coverage_tests(builder, self.compiler, self.target, CoverageMap::MODE); - Self::run_coverage_tests(builder, self.compiler, self.target, CoverageRun::MODE); - } -} - -coverage_test_alias! { - /// Runs the `tests/coverage` test suite in "coverage-map" mode only. - /// Used by `x test` and `x test coverage-map`. - CoverageMap { - alias_and_mode: "coverage-map", - default: true, - only_hosts: false, - } -} -coverage_test_alias! { - /// Runs the `tests/coverage` test suite in "coverage-run" mode only. - /// Used by `x test` and `x test coverage-run`. - CoverageRun { - alias_and_mode: "coverage-run", - default: true, - // Compiletest knows how to automatically skip these tests when cross-compiling, - // but skipping the whole step here makes it clearer that they haven't run at all. - only_hosts: true, - } -} - test!(CoverageRunRustdoc { path: "tests/coverage-run-rustdoc", mode: "coverage-run", diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index db2c4744eb08..da2ce5b59763 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -944,8 +944,6 @@ impl<'a> Builder<'a> { test::Ui, test::Crashes, test::Coverage, - test::CoverageMap, - test::CoverageRun, test::MirOpt, test::Codegen, test::CodegenUnits, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 5769198afac6..0c27597083de 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -828,3 +828,36 @@ fn test_test_compiler() { assert_eq!((compiler, cranelift, gcc), (true, false, false)); } + +#[test] +fn test_test_coverage() { + struct Case { + cmd: &'static [&'static str], + expected: &'static [&'static str], + } + let cases = &[ + Case { cmd: &["test"], expected: &["coverage-map", "coverage-run"] }, + Case { cmd: &["test", "coverage"], expected: &["coverage-map", "coverage-run"] }, + Case { cmd: &["test", "coverage-map"], expected: &["coverage-map"] }, + Case { cmd: &["test", "coverage-run"], expected: &["coverage-run"] }, + Case { cmd: &["test", "coverage", "--skip=coverage"], expected: &[] }, + Case { cmd: &["test", "coverage", "--skip=tests/coverage"], expected: &[] }, + Case { cmd: &["test", "coverage", "--skip=coverage-map"], expected: &["coverage-run"] }, + Case { cmd: &["test", "coverage", "--skip=coverage-run"], expected: &["coverage-map"] }, + Case { cmd: &["test", "--skip=coverage-map", "--skip=coverage-run"], expected: &[] }, + Case { cmd: &["test", "coverage", "--skip=tests"], expected: &[] }, + ]; + + for &Case { cmd, expected } in cases { + // Print each test case so that if one fails, the most recently printed + // case is the one that failed. + println!("Testing case: {cmd:?}"); + let cmd = cmd.iter().copied().map(str::to_owned).collect::>(); + let config = configure_with_args(&cmd, &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]); + let mut cache = run_build(&config.paths.clone(), config); + + let modes = + cache.all::().iter().map(|(step, ())| step.mode).collect::>(); + assert_eq!(modes, expected); + } +} diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index 2aad5650fa89..01bac1498c24 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -129,8 +129,19 @@ pub fn get_closest_merge_commit( git.current_dir(git_dir); } + let channel = include_str!("../../ci/channel"); + let merge_base = { - if CiEnv::is_ci() { + if CiEnv::is_ci() && + // FIXME: When running on rust-lang managed CI and it's not a nightly build, + // `git_upstream_merge_base` fails with an error message similar to this: + // ``` + // called `Result::unwrap()` on an `Err` value: "command did not execute successfully: + // cd \"/checkout\" && \"git\" \"merge-base\" \"origin/master\" \"HEAD\"\nexpected success, got: exit status: 1\n" + // ``` + // Investigate and resolve this issue instead of skipping it like this. + (channel == "nightly" || !CiEnv::is_rust_lang_managed_ci_job()) + { git_upstream_merge_base(config, git_dir).unwrap() } else { // For non-CI environments, ignore rust-lang/rust upstream as it usually gets diff --git a/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch b/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch index 7ae4469428b1..1ae0ecf6cb5e 100644 --- a/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch +++ b/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch @@ -10,7 +10,7 @@ https://sourceware.org/bugzilla/show_bug.cgi?id=28509 And this is the first version of the proposed binutils patch, https://sourceware.org/pipermail/binutils/2021-November/118398.html -After applying the binutils patch, I get the the unexpected error when +After applying the binutils patch, I get the unexpected error when building libgcc, /scratch/nelsonc/riscv-gnu-toolchain/riscv-gcc/libgcc/config/riscv/div.S:42: diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 01fabfb39a77..d7e368cc87f2 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -60,6 +60,7 @@ - [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md) - [loongarch\*-unknown-none\*](platform-support/loongarch-none.md) - [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md) + - [m68k-unknown-none-elf](platform-support/m68k-unknown-none-elf.md) - [mips64-openwrt-linux-musl](platform-support/mips64-openwrt-linux-musl.md) - [mipsel-sony-psx](platform-support/mipsel-sony-psx.md) - [mips\*-mti-none-elf](platform-support/mips-mti-none-elf.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 1d202402288c..a68efcda1f30 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -317,6 +317,7 @@ target | std | host | notes [`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI] [`loongarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | LoongArch64 OpenHarmony [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux +[`m68k-unknown-none-elf`](platform-support/m68k-unknown-none-elf.md) | | | Motorola 680x0 `mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23) `mips-unknown-linux-musl` | ✓ | | MIPS Linux with musl 1.2.3 `mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc diff --git a/src/doc/rustc/src/platform-support/m68k-unknown-none-elf.md b/src/doc/rustc/src/platform-support/m68k-unknown-none-elf.md new file mode 100644 index 000000000000..92780cb5a5ca --- /dev/null +++ b/src/doc/rustc/src/platform-support/m68k-unknown-none-elf.md @@ -0,0 +1,108 @@ +# m68k-unknown-none-elf + +**Tier: 3** + +Bare metal Motorola 680x0 + +## Designated Developers + +* [@knickish](https://github.com/knickish) + +## Requirements + +This target requires an m68k build environment for cross-compilation which +is available on Debian, Debian-based systems, openSUSE, and other distributions. + +On Debian-based systems, it should be sufficient to install a g++ cross-compiler for the m68k +architecture which will automatically pull in additional dependencies such as +the glibc cross development package: + +```sh +apt install g++-m68k-linux-gnu +``` + +Binaries can be run using QEMU user emulation. On Debian-based systems, it should be +sufficient to install the package `qemu-user-static` to be able to run simple static +binaries: + +```text +# apt install qemu-user-static +``` + +To run more complex programs, it will be necessary to set up a Debian/m68k chroot with +the help of the command `debootstrap`: + +```text +# apt install debootstrap debian-ports-archive-keyring +# debootstrap --keyring=/usr/share/keyrings/debian-ports-archive-keyring.gpg --arch=m68k unstable debian-68k http://ftp.ports.debian.org/debian-ports +``` + +This chroot can then seamlessly entered using the normal `chroot` command thanks to +QEMU user emulation: + +```text +# chroot /path/to/debian-68k +``` + +To get started with native builds, which are currently untested, a native Debian/m68k +system can be installed either on real hardware such as 68k-based Commodore Amiga or +Atari systems or emulated environments such as QEMU version 4.2 or newer or ARAnyM. + +ISO images for installation are provided by the Debian Ports team and can be obtained +from the Debian CD image server available at: + +[https://cdimage.debian.org/cdimage/ports/current](https://cdimage.debian.org/cdimage/ports/current/) + +Documentation for Debian/m68k is available on the Debian Wiki at: + +[https://wiki.debian.org/M68k](https://wiki.debian.org/M68k) + +Support is available either through the `debian-68k` mailing list: + +[https://lists.debian.org/debian-68k/](https://lists.debian.org/debian-68k/) + +or the `#debian-68k` IRC channel on OFTC network. + +## Building + +At least llvm version `19.1.5` is required to build `core` and `alloc` for this target, and currently the gnu linker is required, as `lld` has no support for the `m68k` architecture + +## Cross-compilation + +This target can be cross-compiled from a standard Debian or Debian-based, openSUSE or any +other distribution which has a basic m68k cross-toolchain available. + +## Testing + +Currently there is no support to run the rustc test suite for this target. + +## Building Rust programs + +Recommended `.cargo/config.toml`: +```toml +[unstable] +build-std = ["panic_abort", "core", "alloc"] + +[target.m68k-unknown-none-elf] +# as we're building for ELF, the m68k-linux linker should be adequate +linker = "m68k-linux-gnu-ld" + +# the mold linker also supports m68k, remove the above line and uncomment the +# following ones to use that instead +# linker = "clang" +# rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold/binary"] +``` + +Rust programs can be built for this target using: + +```sh +cargo build --target m68k-unknown-none-elf +``` + +Very simple programs can be run using the `qemu-m68k-static` program: + +```sh +qemu-m68k-static your-code +``` + +For more complex applications, a chroot or native m68k system is required for testing. diff --git a/src/doc/unstable-book/src/compiler-flags/emscripten-wasm-eh.md b/src/doc/unstable-book/src/compiler-flags/emscripten-wasm-eh.md new file mode 100644 index 000000000000..eab29a1744b6 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/emscripten-wasm-eh.md @@ -0,0 +1,6 @@ +# `emscripten-wasm-eh` + +Use the WebAssembly exception handling ABI to unwind for the +`wasm32-unknown-emscripten`. If compiling with this setting, the `emcc` linker +should be invoked with `-fwasm-exceptions`. If linking with C/C++ files, the +C/C++ files should also be compiled with `-fwasm-exceptions`. diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 8aeebdde7bb4..1fb35750c15f 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -320,6 +320,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { ); return Symbol::intern("()"); } + PatKind::Guard(p, _) => return name_from_pat(&*p), PatKind::Range(..) => return kw::Underscore, PatKind::Slice(begin, ref mid, end) => { let begin = begin.iter().map(|p| name_from_pat(p).to_string()); diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index e64baca974da..7a95d33723e5 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -1,5 +1,6 @@ use std::mem; +use rustc_attr_parsing::StabilityLevel; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; use rustc_middle::ty::{self, TyCtxt}; @@ -306,7 +307,12 @@ impl DocFolder for CacheBuilder<'_, '_> { | clean::ProcMacroItem(..) | clean::VariantItem(..) => { use rustc_data_structures::fx::IndexEntry as Entry; - if !self.cache.stripped_mod { + if !self.cache.stripped_mod + && !matches!( + item.stability.map(|stab| stab.level), + Some(StabilityLevel::Stable { allowed_through_unstable_modules: true, .. }) + ) + { // Re-exported items mean that the same id can show up twice // in the rustdoc ast that we're looking at. We know, // however, that a re-exported item doesn't show up in the diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index af39d15f6717..881df8b00501 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -357,7 +357,7 @@ fn sidebar_type_alias<'a>( deref_id_map: &'a DefIdMap, ) { if let Some(inner_type) = &t.inner_type { - items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type"), "type")); + items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased Type"), "type")); match inner_type { clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => { let mut variants = variants diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index d74dcc98cb05..96ca96ee6bc1 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -865,11 +865,11 @@ fn main_args( } let krate = rustc_interface::passes::parse(sess); - if sess.dcx().has_errors().is_some() { - sess.dcx().fatal("Compilation failed, aborting rustdoc"); - } - rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { + if sess.dcx().has_errors().is_some() { + sess.dcx().fatal("Compilation failed, aborting rustdoc"); + } + let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || { core::run_global_ctxt(tcx, show_coverage, render_options, output_format) }); diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index d6ca03f76430..feec2a7444f8 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -114,6 +114,7 @@ static TARGETS: &[&str] = &[ "loongarch64-unknown-none", "loongarch64-unknown-none-softfloat", "m68k-unknown-linux-gnu", + "m68k-unknown-none-elf", "csky-unknown-linux-gnuabiv2", "csky-unknown-linux-gnuabiv2hf", "mips-unknown-linux-gnu", diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index fb9f2b1526e3..9c8edfd6113f 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -55,7 +55,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { | PatKind::Err(_) => false, PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), - PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) => unary_pattern(x), + PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), PatKind::Path(_) | PatKind::Lit(_) => true, } } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index b72a61a43849..4b731d759723 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -254,9 +254,11 @@ impl<'a> NormalizedPat<'a> { fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { match pat.kind { PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, - PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Deref(pat) | PatKind::Ref(pat, _) => { - Self::from_pat(cx, arena, pat) - }, + PatKind::Binding(.., Some(pat)) + | PatKind::Box(pat) + | PatKind::Deref(pat) + | PatKind::Ref(pat, _) + | PatKind::Guard(pat, _) => Self::from_pat(cx, arena, pat), PatKind::Never => Self::Never, PatKind::Struct(ref path, fields, _) => { let fields = diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 3ca20479f8e0..10ca6832d9c1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -343,6 +343,10 @@ impl<'a> PatState<'a> { matches!(self, Self::Wild) }, + PatKind::Guard(..) => { + matches!(self, Self::Wild) + } + // Patterns for things which can only contain a single sub-pattern. PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => { self.add_pat(cx, pat) diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index d2970c93f8e9..c2dcb5ae1f9e 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -712,6 +712,12 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Ref({pat}, Mutability::{muta:?})"); self.pat(pat); }, + PatKind::Guard(pat, cond) => { + bind!(self, pat, cond); + kind!("Guard({pat}, {cond})"); + self.pat(pat); + self.expr(cond); + } PatKind::Lit(lit_expr) => { bind!(self, lit_expr); kind!("Lit({lit_expr})"); diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index ed52c481de12..7c4e834f8416 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1104,6 +1104,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_pat(pat); std::mem::discriminant(&mu).hash(&mut self.s); }, + PatKind::Guard(pat, guard) => { + self.hash_pat(pat); + self.hash_expr(guard); + }, PatKind::Slice(l, m, r) => { for pat in l { self.hash_pat(pat); diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 30e7471dd925..ba4a0f44584e 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1777,7 +1777,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, } }, - PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) => true, + PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true, } } diff --git a/src/tools/compiletest/src/compute_diff.rs b/src/tools/compiletest/src/compute_diff.rs index 92c80c27de03..4c942c51bae1 100644 --- a/src/tools/compiletest/src/compute_diff.rs +++ b/src/tools/compiletest/src/compute_diff.rs @@ -31,7 +31,7 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec { + diff::Result::Left(s) => { if lines_since_mismatch >= context_size && lines_since_mismatch > 0 { results.push(mismatch); mismatch = Mismatch::new(line_number - context_queue.len() as u32); @@ -41,11 +41,11 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec { + diff::Result::Right(s) => { if lines_since_mismatch >= context_size && lines_since_mismatch > 0 { results.push(mismatch); mismatch = Mismatch::new(line_number - context_queue.len() as u32); @@ -55,18 +55,18 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec { + diff::Result::Both(s, _) => { if context_queue.len() >= context_size { let _ = context_queue.pop_front(); } if lines_since_mismatch < context_size { - mismatch.lines.push(DiffLine::Context(str.to_owned())); + mismatch.lines.push(DiffLine::Context(s.to_owned())); } else if context_size > 0 { - context_queue.push_back(str); + context_queue.push_back(s); } line_number += 1; diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml index d0413911014f..cea234cc74cb 100644 --- a/src/tools/opt-dist/Cargo.toml +++ b/src/tools/opt-dist/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" build_helper = { path = "../../build_helper" } env_logger = "0.11" log = "0.4" -anyhow = { version = "1", features = ["backtrace"] } +anyhow = "1" humantime = "2" humansize = "2" sysinfo = { version = "0.31.2", default-features = false, features = ["disk"] } diff --git a/src/tools/replace-version-placeholder/src/main.rs b/src/tools/replace-version-placeholder/src/main.rs index 247e4e7e9323..88118cab2358 100644 --- a/src/tools/replace-version-placeholder/src/main.rs +++ b/src/tools/replace-version-placeholder/src/main.rs @@ -11,12 +11,7 @@ fn main() { let version_str = version_str.trim(); walk::walk_many( &[&root_path.join("compiler"), &root_path.join("library")], - |path, _is_dir| { - walk::filter_dirs(path) - // We exempt these as they require the placeholder - // for their operation - || path.ends_with("compiler/rustc_attr/src/builtin.rs") - }, + |path, _is_dir| walk::filter_dirs(path), &mut |entry, contents| { if !contents.contains(VERSION_PLACEHOLDER) { return; diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 46bae20054cf..ec33009239c4 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -6,6 +6,10 @@ on: pull_request: merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 @@ -103,7 +107,7 @@ jobs: - name: Run analysis-stats on the rust standard libraries if: matrix.os == 'ubuntu-latest' env: - RUSTC_BOOTSTRAP: 1 + RUSTC_BOOTSTRAP: 1 run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps --no-sysroot --no-test $(rustc --print sysroot)/lib/rustlib/src/rust/library/ - name: clippy diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 2323fdf5333e..48b5f3aabfc1 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1375,7 +1375,6 @@ dependencies = [ "memmap2", "object 0.33.0", "paths", - "proc-macro-api", "proc-macro-test", "ra-ap-rustc_lexer", "span", @@ -1390,6 +1389,7 @@ version = "0.0.0" dependencies = [ "proc-macro-api", "proc-macro-srv", + "tt", ] [[package]] diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 7f3abcccc472..9440123de70d 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -79,6 +79,7 @@ span = { path = "./crates/span", version = "0.0.0" } stdx = { path = "./crates/stdx", version = "0.0.0" } syntax = { path = "./crates/syntax", version = "0.0.0" } syntax-bridge = { path = "./crates/syntax-bridge", version = "0.0.0" } +test-utils = { path = "./crates/test-utils", version = "0.0.0" } toolchain = { path = "./crates/toolchain", version = "0.0.0" } tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } @@ -93,7 +94,6 @@ ra-ap-rustc_pattern_analysis = { version = "0.87", default-features = false } # local crates that aren't published to crates.io. These should not have versions. test-fixture = { path = "./crates/test-fixture" } -test-utils = { path = "./crates/test-utils" } # In-tree crates that are published separately and follow semver. See lib/README.md line-index = { version = "0.1.2" } @@ -203,6 +203,13 @@ new_ret_no_self = "allow" useless_asref = "allow" # Has false positives assigning_clones = "allow" +# Does not work with macros +vec_init_then_push = "allow" +# Our tests have a lot of these +literal_string_with_formatting_args = "allow" +# This lint has been empowered but now also triggers on cases where its invalid to do so +# due to it ignoring move analysis +unnecessary_map_or = "allow" ## Following lints should be tackled at some point too_many_arguments = "allow" diff --git a/src/tools/rust-analyzer/clippy.toml b/src/tools/rust-analyzer/clippy.toml index 8032c775ab0a..1046cb3d56bc 100644 --- a/src/tools/rust-analyzer/clippy.toml +++ b/src/tools/rust-analyzer/clippy.toml @@ -3,3 +3,7 @@ disallowed-types = [ { path = "std::collections::HashSet", reason = "use FxHashSet" }, { path = "std::collections::hash_map::RandomState", reason = "use BuildHasherDefault"} ] + +disallowed-methods = [ + { path = "std::process::Command::new", reason = "use `toolchain::command` instead as it forces the choice of a working directory" }, +] diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index e86944eeb352..a0fc8c31eaf6 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -490,21 +490,25 @@ impl CrateGraph { } } - pub fn sort_deps(&mut self) { - self.arena - .iter_mut() - .for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id)); - } - /// Extends this crate graph by adding a complete second crate /// graph and adjust the ids in the [`ProcMacroPaths`] accordingly. /// + /// This will deduplicate the crates of the graph where possible. + /// Furthermore dependencies are sorted by crate id to make deduplication easier. + /// /// Returns a map mapping `other`'s IDs to the new IDs in `self`. pub fn extend( &mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths, ) -> FxHashMap { + // Sorting here is a bit pointless because the input is likely already sorted. + // However, the overhead is small and it makes the `extend` method harder to misuse. + self.arena + .iter_mut() + .for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id)); + + let m = self.len(); let topo = other.crates_in_topological_order(); let mut id_map: FxHashMap = FxHashMap::default(); for topo in topo { @@ -513,7 +517,8 @@ impl CrateGraph { crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]); crate_data.dependencies.sort_by_key(|dep| dep.crate_id); - let new_id = self.arena.alloc(crate_data.clone()); + let find = self.arena.iter().take(m).find_map(|(k, v)| (v == crate_data).then_some(k)); + let new_id = find.unwrap_or_else(|| self.arena.alloc(crate_data.clone())); id_map.insert(topo, new_id); } diff --git a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs index 35c0c89c70cf..84b91a527f05 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs @@ -45,8 +45,8 @@ impl From for CfgExpr { impl CfgExpr { #[cfg(feature = "tt")] - pub fn parse(tt: &tt::Subtree) -> CfgExpr { - next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) + pub fn parse(tt: &tt::TopSubtree) -> CfgExpr { + next_cfg_expr(&mut tt.iter()).unwrap_or(CfgExpr::Invalid) } /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. @@ -66,19 +66,19 @@ impl CfgExpr { } #[cfg(feature = "tt")] -fn next_cfg_expr(it: &mut std::slice::Iter<'_, tt::TokenTree>) -> Option { +fn next_cfg_expr(it: &mut tt::iter::TtIter<'_, S>) -> Option { use intern::sym; + use tt::iter::TtElement; let name = match it.next() { None => return None, - Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), + Some(TtElement::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), Some(_) => return Some(CfgExpr::Invalid), }; - // Peek - let ret = match it.as_slice().first() { - Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => { - match it.as_slice().get(1) { + let ret = match it.peek() { + Some(TtElement::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => { + match it.remaining().flat_tokens().get(1) { Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => { it.next(); it.next(); @@ -87,9 +87,8 @@ fn next_cfg_expr(it: &mut std::slice::Iter<'_, tt::TokenTree>) -> Option return Some(CfgExpr::Invalid), } } - Some(tt::TokenTree::Subtree(subtree)) => { + Some(TtElement::Subtree(_, mut sub_it)) => { it.next(); - let mut sub_it = subtree.token_trees.iter(); let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it)); match name { s if s == sym::all => CfgExpr::All(subs.collect()), @@ -104,7 +103,7 @@ fn next_cfg_expr(it: &mut std::slice::Iter<'_, tt::TokenTree>) -> Option bool { self.by_key(&sym::doc).tt_values().any(|tt| { - tt.delimiter.kind == DelimiterKind::Parenthesis && - matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::hidden) + tt.top_subtree().delimiter.kind == DelimiterKind::Parenthesis && + matches!(tt.token_trees().flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::hidden) }) } pub fn has_doc_notable_trait(&self) -> bool { self.by_key(&sym::doc).tt_values().any(|tt| { - tt.delimiter.kind == DelimiterKind::Parenthesis && - matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::notable_trait) + tt.top_subtree().delimiter.kind == DelimiterKind::Parenthesis && + matches!(tt.token_trees().flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::notable_trait) }) } @@ -245,8 +246,8 @@ impl From for DocExpr { } impl DocExpr { - fn parse(tt: &tt::Subtree) -> DocExpr { - next_doc_expr(&mut tt.token_trees.iter()).unwrap_or(DocExpr::Invalid) + fn parse(tt: &tt::TopSubtree) -> DocExpr { + next_doc_expr(tt.iter()).unwrap_or(DocExpr::Invalid) } pub fn aliases(&self) -> &[Symbol] { @@ -260,32 +261,29 @@ impl DocExpr { } } -fn next_doc_expr(it: &mut slice::Iter<'_, tt::TokenTree>) -> Option { +fn next_doc_expr(mut it: TtIter<'_, S>) -> Option { let name = match it.next() { None => return None, - Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), + Some(TtElement::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), Some(_) => return Some(DocExpr::Invalid), }; // Peek - let ret = match it.as_slice().first() { - Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => { - match it.as_slice().get(1) { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + let ret = match it.peek() { + Some(TtElement::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => { + it.next(); + match it.next() { + Some(TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, kind: tt::LitKind::Str, .. - }))) => { - it.next(); - it.next(); - DocAtom::KeyValue { key: name, value: text.clone() }.into() - } + }))) => DocAtom::KeyValue { key: name, value: text.clone() }.into(), _ => return Some(DocExpr::Invalid), } } - Some(tt::TokenTree::Subtree(subtree)) => { + Some(TtElement::Subtree(_, subtree_iter)) => { it.next(); - let subs = parse_comma_sep(subtree); + let subs = parse_comma_sep(subtree_iter); match &name { s if *s == sym::alias => DocExpr::Alias(subs), _ => DocExpr::Invalid, @@ -293,29 +291,17 @@ fn next_doc_expr(it: &mut slice::Iter<'_, tt::TokenTree>) -> Option DocAtom::Flag(name).into(), }; - - // Eat comma separator - if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = it.as_slice().first() { - if punct.char == ',' { - it.next(); - } - } Some(ret) } -fn parse_comma_sep(subtree: &tt::Subtree) -> Vec { - subtree - .token_trees - .iter() - .filter_map(|tt| match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - kind: tt::LitKind::Str, - symbol, - .. - })) => Some(symbol.clone()), - _ => None, - }) - .collect() +fn parse_comma_sep(iter: TtIter<'_, S>) -> Vec { + iter.filter_map(|tt| match tt { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { + kind: tt::LitKind::Str, symbol, .. + })) => Some(symbol.clone()), + _ => None, + }) + .collect() } impl AttrsWithOwner { @@ -563,7 +549,7 @@ pub struct AttrQuery<'attr> { } impl<'attr> AttrQuery<'attr> { - pub fn tt_values(self) -> impl Iterator { + pub fn tt_values(self) -> impl Iterator { self.attrs().filter_map(|attr| attr.token_tree_value()) } @@ -585,7 +571,7 @@ impl<'attr> AttrQuery<'attr> { pub fn attrs(self) -> impl Iterator + Clone { let key = self.key; - self.attrs.iter().filter(move |attr| attr.path.as_ident().map_or(false, |s| *s == *key)) + self.attrs.iter().filter(move |attr| attr.path.as_ident().is_some_and(|s| *s == *key)) } /// Find string value for a specific key inside token tree @@ -596,12 +582,12 @@ impl<'attr> AttrQuery<'attr> { /// ``` pub fn find_string_value_in_tt(self, key: &'attr Symbol) -> Option<&'attr str> { self.tt_values().find_map(|tt| { - let name = tt.token_trees.iter() - .skip_while(|tt| !matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == *key)) + let name = tt.iter() + .skip_while(|tt| !matches!(tt, TtElement::Leaf(tt::Leaf::Ident(tt::Ident { sym, ..} )) if *sym == *key)) .nth(2); match name { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ symbol: text, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_) , ..}))) => Some(text.as_str()), + Some(TtElement::Leaf(tt::Leaf::Literal(tt::Literal{ symbol: text, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_) , ..}))) => Some(text.as_str()), _ => None } }) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index eed9f9468fd9..10b84d041bde 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -2216,11 +2216,11 @@ impl ExprCollector<'_> { }; // This needs to match `Flag` in library/core/src/fmt/rt.rs. let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32) - | ((sign == Some(FormatSign::Minus)) as u32) << 1 - | (alternate as u32) << 2 - | (zero_pad as u32) << 3 - | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4 - | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5; + | (((sign == Some(FormatSign::Minus)) as u32) << 1) + | ((alternate as u32) << 2) + | ((zero_pad as u32) << 3) + | (((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4) + | (((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5); let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( flags as u128, Some(BuiltinUint::U32), @@ -2468,7 +2468,7 @@ impl ExprCollector<'_> { fn comma_follows_token(t: Option) -> bool { (|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))() - .map_or(false, |it| it.kind() == syntax::T![,]) + .is_some_and(|it| it.kind() == syntax::T![,]) } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs index 13ba4db60649..7e15a9f2d618 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs @@ -52,6 +52,7 @@ fn your_stack_belongs_to_me() { cov_mark::check!(your_stack_belongs_to_me); lower( r#" +#![recursion_limit = "32"] macro_rules! n_nuple { ($e:tt) => (); ($($rest:tt)*) => {{ @@ -68,6 +69,7 @@ fn your_stack_belongs_to_me2() { cov_mark::check!(overflow_but_not_me); lower( r#" +#![recursion_limit = "32"] macro_rules! foo { () => {{ foo!(); foo!(); }} } @@ -78,8 +80,6 @@ fn main() { foo!(); } #[test] fn recursion_limit() { - cov_mark::check!(your_stack_belongs_to_me); - lower( r#" #![recursion_limit = "2"] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index 15dd6aba311f..d85bc9a4320f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -11,6 +11,7 @@ use la_arena::{Idx, RawIdx}; use smallvec::SmallVec; use syntax::{ast, Parse}; use triomphe::Arc; +use tt::iter::TtElement; use crate::{ db::DefDatabase, @@ -156,20 +157,21 @@ impl FunctionData { } } -fn parse_rustc_legacy_const_generics(tt: &crate::tt::Subtree) -> Box<[u32]> { +fn parse_rustc_legacy_const_generics(tt: &crate::tt::TopSubtree) -> Box<[u32]> { let mut indices = Vec::new(); - for args in tt.token_trees.chunks(2) { - match &args[0] { - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => match lit.symbol.as_str().parse() { + let mut iter = tt.iter(); + while let (Some(first), second) = (iter.next(), iter.next()) { + match first { + TtElement::Leaf(tt::Leaf::Literal(lit)) => match lit.symbol.as_str().parse() { Ok(index) => indices.push(index), Err(_) => break, }, _ => break, } - if let Some(comma) = args.get(1) { + if let Some(comma) = second { match comma { - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if punct.char == ',' => {} + TtElement::Leaf(tt::Leaf::Punct(punct)) if punct.char == ',' => {} _ => break, } } @@ -227,20 +229,24 @@ impl TypeAliasData { } } +bitflags::bitflags! { + #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] + pub struct TraitFlags: u8 { + const IS_AUTO = 1 << 0; + const IS_UNSAFE = 1 << 1; + const IS_FUNDAMENTAL = 1 << 2; + const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 3; + const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 4; + const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 5; + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct TraitData { pub name: Name, pub items: Vec<(Name, AssocItemId)>, - pub is_auto: bool, - pub is_unsafe: bool, - pub rustc_has_incoherent_inherent_impls: bool, - pub skip_array_during_method_dispatch: bool, - pub skip_boxed_slice_during_method_dispatch: bool, - pub fundamental: bool, + pub flags: TraitFlags, pub visibility: RawVisibility, - /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore - /// method calls to this trait's methods when the receiver is an array and the crate edition is - /// 2015 or 2018. // box it as the vec is usually empty anyways pub macro_calls: Option, MacroCallId)>>>, } @@ -259,42 +265,50 @@ impl TraitData { let item_tree = tree_id.item_tree(db); let tr_def = &item_tree[tree_id.value]; let name = tr_def.name.clone(); - let is_auto = tr_def.is_auto; - let is_unsafe = tr_def.is_unsafe; let visibility = item_tree[tr_def.visibility].clone(); let attrs = item_tree.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into()); + + let mut flags = TraitFlags::empty(); + + if tr_def.is_auto { + flags |= TraitFlags::IS_AUTO; + } + if tr_def.is_unsafe { + flags |= TraitFlags::IS_UNSAFE; + } + if attrs.by_key(&sym::fundamental).exists() { + flags |= TraitFlags::IS_FUNDAMENTAL; + } + if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() { + flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS; + } + let mut skip_array_during_method_dispatch = attrs.by_key(&sym::rustc_skip_array_during_method_dispatch).exists(); let mut skip_boxed_slice_during_method_dispatch = false; for tt in attrs.by_key(&sym::rustc_skip_during_method_dispatch).tt_values() { - for tt in tt.token_trees.iter() { - if let crate::tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = tt { + for tt in tt.iter() { + if let tt::iter::TtElement::Leaf(tt::Leaf::Ident(ident)) = tt { skip_array_during_method_dispatch |= ident.sym == sym::array; skip_boxed_slice_during_method_dispatch |= ident.sym == sym::boxed_slice; } } } - let rustc_has_incoherent_inherent_impls = - attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists(); - let fundamental = attrs.by_key(&sym::fundamental).exists(); + + if skip_array_during_method_dispatch { + flags |= TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH; + } + if skip_boxed_slice_during_method_dispatch { + flags |= TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH; + } + let mut collector = AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr)); collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items); let (items, macro_calls, diagnostics) = collector.finish(); ( - Arc::new(TraitData { - name, - macro_calls, - items, - is_auto, - is_unsafe, - visibility, - skip_array_during_method_dispatch, - skip_boxed_slice_during_method_dispatch, - rustc_has_incoherent_inherent_impls, - fundamental, - }), + Arc::new(TraitData { name, macro_calls, items, visibility, flags }), DefDiagnostics::new(diagnostics), ) } @@ -421,7 +435,7 @@ impl Macro2Data { .by_key(&sym::rustc_builtin_macro) .tt_values() .next() - .and_then(|attr| parse_macro_name_and_helper_attrs(&attr.token_trees)) + .and_then(parse_macro_name_and_helper_attrs) .map(|(_, helpers)| helpers); Arc::new(Macro2Data { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index 068ebb3b7e91..8fc19854033c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -10,6 +10,7 @@ use intern::sym; use la_arena::Arena; use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; use triomphe::Arc; +use tt::iter::TtElement; use crate::{ builtin_type::{BuiltinInt, BuiltinUint}, @@ -20,7 +21,7 @@ use crate::{ }, lang_item::LangItem, nameres::diagnostics::{DefDiagnostic, DefDiagnostics}, - tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}, + tt::{Delimiter, DelimiterKind, Leaf, TopSubtree}, type_ref::{TypeRefId, TypesMap}, visibility::RawVisibility, EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, VariantId, @@ -95,8 +96,8 @@ fn repr_from_value( item_tree.attrs(db, krate, of).by_key(&sym::repr).tt_values().find_map(parse_repr_tt) } -fn parse_repr_tt(tt: &Subtree) -> Option { - match tt.delimiter { +fn parse_repr_tt(tt: &TopSubtree) -> Option { + match tt.top_subtree().delimiter { Delimiter { kind: DelimiterKind::Parenthesis, .. } => {} _ => return None, } @@ -106,14 +107,14 @@ fn parse_repr_tt(tt: &Subtree) -> Option { let mut max_align: Option = None; let mut min_pack: Option = None; - let mut tts = tt.token_trees.iter().peekable(); + let mut tts = tt.iter(); while let Some(tt) = tts.next() { - if let TokenTree::Leaf(Leaf::Ident(ident)) = tt { + if let TtElement::Leaf(Leaf::Ident(ident)) = tt { flags.insert(match &ident.sym { s if *s == sym::packed => { - let pack = if let Some(TokenTree::Subtree(tt)) = tts.peek() { + let pack = if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() { tts.next(); - if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() { + if let Some(TtElement::Leaf(Leaf::Literal(lit))) = tt_iter.next() { lit.symbol.as_str().parse().unwrap_or_default() } else { 0 @@ -127,9 +128,9 @@ fn parse_repr_tt(tt: &Subtree) -> Option { ReprFlags::empty() } s if *s == sym::align => { - if let Some(TokenTree::Subtree(tt)) = tts.peek() { + if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() { tts.next(); - if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() { + if let Some(TtElement::Leaf(Leaf::Literal(lit))) = tt_iter.next() { if let Ok(align) = lit.symbol.as_str().parse() { let align = Align::from_bytes(align).ok(); max_align = max_align.max(align); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index d7e83ce33e89..bf6cc1dcadec 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -21,6 +21,7 @@ use crate::{ item_tree::{AttrOwner, ItemTree, ItemTreeSourceMaps}, lang_item::{self, LangItem, LangItemTarget, LangItems}, nameres::{diagnostics::DefDiagnostics, DefMap}, + tt, type_ref::TypesSourceMap, visibility::{self, Visibility}, AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId, @@ -294,14 +295,14 @@ fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool { // This is a `cfg_attr`; check if it could possibly expand to `no_std`. // Syntax is: `#[cfg_attr(condition(cfg, style), attr0, attr1, <...>)]` let tt = match attr.token_tree_value() { - Some(tt) => &tt.token_trees, + Some(tt) => tt.token_trees(), None => continue, }; let segments = - tt.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',')); + tt.split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(p)) if p.char == ',')); for output in segments.skip(1) { - match output { + match output.flat_tokens() { [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.sym == sym::no_std => { return true } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs index 22005695af63..0f73595347b1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs @@ -90,7 +90,7 @@ pub mod keys { map.map.get::, ID>>()?.get(key) } fn is_empty(map: &DynMap) -> bool { - map.map.get::, ID>>().map_or(true, |it| it.is_empty()) + map.map.get::, ID>>().is_none_or(|it| it.is_empty()) } } } @@ -141,7 +141,7 @@ impl Policy for (K, V) { map.map.get::>()?.get(key) } fn is_empty(map: &DynMap) -> bool { - map.map.get::>().map_or(true, |it| it.is_empty()) + map.map.get::>().is_none_or(|it| it.is_empty()) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs index 163211fea526..c31d32213289 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs @@ -2,7 +2,7 @@ use expect_test::expect; -use crate::macro_expansion_tests::check; +use crate::macro_expansion_tests::{check, check_errors}; #[test] fn test_copy_expand_simple() { @@ -16,7 +16,7 @@ struct Foo; #[derive(Copy)] struct Foo; -impl < > $crate::marker::Copy for Foo< > where {}"#]], +impl <> $crate::marker::Copy for Foo< > where {}"#]], ); } @@ -40,7 +40,7 @@ macro Copy {} #[derive(Copy)] struct Foo; -impl < > $crate::marker::Copy for Foo< > where {}"#]], +impl <> $crate::marker::Copy for Foo< > where {}"#]], ); } @@ -225,14 +225,14 @@ enum Bar { Bar, } -impl < > $crate::default::Default for Foo< > where { +impl <> $crate::default::Default for Foo< > where { fn default() -> Self { Foo { field1: $crate::default::Default::default(), field2: $crate::default::Default::default(), } } } -impl < > $crate::default::Default for Bar< > where { +impl <> $crate::default::Default for Bar< > where { fn default() -> Self { Bar::Bar } @@ -260,7 +260,7 @@ enum Command { Jump, } -impl < > $crate::cmp::PartialEq for Command< > where { +impl <> $crate::cmp::PartialEq for Command< > where { fn eq(&self , other: &Self ) -> bool { match (self , other) { (Command::Move { @@ -273,7 +273,7 @@ impl < > $crate::cmp::PartialEq for Command< > where { } } } -impl < > $crate::cmp::Eq for Command< > where {}"#]], +impl <> $crate::cmp::Eq for Command< > where {}"#]], ); } @@ -298,7 +298,7 @@ enum Command { Jump, } -impl < > $crate::cmp::PartialEq for Command< > where { +impl <> $crate::cmp::PartialEq for Command< > where { fn eq(&self , other: &Self ) -> bool { match (self , other) { (Command::Move { @@ -311,7 +311,7 @@ impl < > $crate::cmp::PartialEq for Command< > where { } } } -impl < > $crate::cmp::Eq for Command< > where {}"#]], +impl <> $crate::cmp::Eq for Command< > where {}"#]], ); } @@ -335,7 +335,7 @@ enum Command { Jump, } -impl < > $crate::cmp::PartialOrd for Command< > where { +impl <> $crate::cmp::PartialOrd for Command< > where { fn partial_cmp(&self , other: &Self ) -> $crate::option::Option::Option<$crate::cmp::Ordering> { match $crate::intrinsics::discriminant_value(self ).partial_cmp(&$crate::intrinsics::discriminant_value(other)) { $crate::option::Option::Some($crate::cmp::Ordering::Equal)=> { @@ -370,7 +370,7 @@ impl < > $crate::cmp::PartialOrd for Command< > where { } } } -impl < > $crate::cmp::Ord for Command< > where { +impl <> $crate::cmp::Ord for Command< > where { fn cmp(&self , other: &Self ) -> $crate::cmp::Ordering { match $crate::intrinsics::discriminant_value(self ).cmp(&$crate::intrinsics::discriminant_value(other)) { $crate::cmp::Ordering::Equal=> { @@ -432,7 +432,7 @@ struct Foo { z: (i32, u64), } -impl < > $crate::hash::Hash for Foo< > where { +impl <> $crate::hash::Hash for Foo< > where { fn hash(&self , ra_expand_state: &mut H) { match self { Foo { @@ -470,7 +470,7 @@ enum Command { Jump, } -impl < > $crate::hash::Hash for Command< > where { +impl <> $crate::hash::Hash for Command< > where { fn hash(&self , ra_expand_state: &mut H) { $crate::mem::discriminant(self ).hash(ra_expand_state); match self { @@ -516,7 +516,7 @@ enum Command { Jump, } -impl < > $crate::fmt::Debug for Command< > where { +impl <> $crate::fmt::Debug for Command< > where { fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { match self { Command::Move { @@ -578,7 +578,7 @@ enum HideAndShowEnum { } } -impl < > $crate::fmt::Debug for HideAndShow< > where { +impl <> $crate::fmt::Debug for HideAndShow< > where { fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { match self { HideAndShow { @@ -588,7 +588,7 @@ impl < > $crate::fmt::Debug for HideAndShow< > where { } } } -impl < > $crate::fmt::Debug for HideAndShowEnum< > where { +impl <> $crate::fmt::Debug for HideAndShowEnum< > where { fn fmt(&self , f: &mut $crate::fmt::Formatter) -> $crate::fmt::Result { match self { HideAndShowEnum::AlwaysShow { @@ -640,17 +640,109 @@ enum Bar { Bar, } -impl < > $crate::default::Default for Foo< > where { +impl <> $crate::default::Default for Foo< > where { fn default() -> Self { Foo { field1: $crate::default::Default::default(), field4: $crate::default::Default::default(), } } } -impl < > $crate::default::Default for Bar< > where { +impl <> $crate::default::Default for Bar< > where { fn default() -> Self { Bar::Bar } }"##]], ); } + +#[test] +fn coerce_pointee_expansion() { + check( + r#" +//- minicore: coerce_pointee + +use core::marker::CoercePointee; + +pub trait Trait {} + +#[derive(CoercePointee)] +#[repr(transparent)] +pub struct Foo<'a, T: ?Sized + Trait, #[pointee] U: ?Sized, const N: u32>(T) +where + U: Trait + ToString;"#, + expect![[r#" + +use core::marker::CoercePointee; + +pub trait Trait {} + +#[derive(CoercePointee)] +#[repr(transparent)] +pub struct Foo<'a, T: ?Sized + Trait, #[pointee] U: ?Sized, const N: u32>(T) +where + U: Trait + ToString; +impl $crate::ops::DispatchFromDyn> for Foo where U: Trait +ToString, T: Trait<__S>, __S: ?Sized, __S: Trait<__S> +ToString, U: ::core::marker::Unsize<__S>, T:?Sized+Trait, U:?Sized, {} +impl $crate::ops::CoerceUnsized> for Foo where U: Trait +ToString, T: Trait<__S>, __S: ?Sized, __S: Trait<__S> +ToString, U: ::core::marker::Unsize<__S>, T:?Sized+Trait, U:?Sized, {}"#]], + ); +} + +#[test] +fn coerce_pointee_errors() { + check_errors( + r#" +//- minicore: coerce_pointee + +use core::marker::CoercePointee; + +#[derive(CoercePointee)] +enum Enum {} + +#[derive(CoercePointee)] +struct Struct1; + +#[derive(CoercePointee)] +struct Struct2(); + +#[derive(CoercePointee)] +struct Struct3 {} + +#[derive(CoercePointee)] +struct Struct4(T); + +#[derive(CoercePointee)] +#[repr(transparent)] +struct Struct5(i32); + +#[derive(CoercePointee)] +#[repr(transparent)] +struct Struct6<#[pointee] T: ?Sized, #[pointee] U: ?Sized>(T, U); + +#[derive(CoercePointee)] +#[repr(transparent)] +struct Struct7(T, U); + +#[derive(CoercePointee)] +#[repr(transparent)] +struct Struct8<#[pointee] T, U: ?Sized>(T); + +#[derive(CoercePointee)] +#[repr(transparent)] +struct Struct9(T); + +#[derive(CoercePointee)] +#[repr(transparent)] +struct Struct9<#[pointee] T, U>(T) where T: ?Sized; +"#, + expect![[r#" + 35..72: `CoercePointee` can only be derived on `struct`s + 74..114: `CoercePointee` can only be derived on `struct`s with at least one field + 116..158: `CoercePointee` can only be derived on `struct`s with at least one field + 160..202: `CoercePointee` can only be derived on `struct`s with at least one field + 204..258: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` + 260..326: `CoercePointee` can only be derived on `struct`s that are generic over at least one type + 328..439: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits + 441..530: exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits + 532..621: `derive(CoercePointee)` requires `T` to be marked `?Sized` + 623..690: `derive(CoercePointee)` requires `T` to be marked `?Sized`"#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 0475e40c5b2a..5b9ffdf37bed 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -16,14 +16,16 @@ mod proc_macros; use std::{iter, ops::Range, sync}; +use base_db::SourceDatabase; use expect_test::Expect; use hir_expand::{ db::ExpandDatabase, proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind}, span_map::SpanMapRef, - InFile, MacroFileId, MacroFileIdExt, + InFile, MacroCallKind, MacroFileId, MacroFileIdExt, }; use intern::Symbol; +use itertools::Itertools; use span::{Edition, Span}; use stdx::{format_to, format_to_acc}; use syntax::{ @@ -40,10 +42,40 @@ use crate::{ resolver::HasResolver, src::HasSource, test_db::TestDB, - tt::Subtree, + tt::TopSubtree, AdtId, AsMacroCall, Lookup, ModuleDefId, }; +#[track_caller] +fn check_errors(ra_fixture: &str, expect: Expect) { + let db = TestDB::with_files(ra_fixture); + let krate = db.fetch_test_crate(); + let def_map = db.crate_def_map(krate); + let errors = def_map + .modules() + .flat_map(|module| module.1.scope.all_macro_calls()) + .filter_map(|macro_call| { + let errors = db.parse_macro_expansion_error(macro_call)?; + let errors = errors.err.as_ref()?.render_to_string(&db); + let macro_loc = db.lookup_intern_macro_call(macro_call); + let ast_id = match macro_loc.kind { + MacroCallKind::FnLike { ast_id, .. } => ast_id.map(|it| it.erase()), + MacroCallKind::Derive { ast_id, .. } => ast_id.map(|it| it.erase()), + MacroCallKind::Attr { ast_id, .. } => ast_id.map(|it| it.erase()), + }; + let ast = db + .parse(ast_id.file_id.file_id().expect("macros inside macros are not supported")) + .syntax_node(); + let ast_id_map = db.ast_id_map(ast_id.file_id); + let node = ast_id_map.get_erased(ast_id.value).to_node(&ast); + Some((node.text_range(), errors)) + }) + .sorted_unstable_by_key(|(range, _)| range.start()) + .format_with("\n", |(range, err), format| format(&format_args!("{range:?}: {err}"))) + .to_string(); + expect.assert_eq(&errors); +} + #[track_caller] fn check(ra_fixture: &str, mut expect: Expect) { let extra_proc_macros = vec![( @@ -245,7 +277,9 @@ fn pretty_print_macro_expansion( let mut res = String::new(); let mut prev_kind = EOF; let mut indent_level = 0; - for token in iter::successors(expn.first_token(), |t| t.next_token()) { + for token in iter::successors(expn.first_token(), |t| t.next_token()) + .take_while(|token| token.text_range().start() < expn.text_range().end()) + { let curr_kind = token.kind(); let space = match (prev_kind, curr_kind) { _ if prev_kind.is_trivia() || curr_kind.is_trivia() => "", @@ -313,14 +347,14 @@ struct IdentityWhenValidProcMacroExpander; impl ProcMacroExpander for IdentityWhenValidProcMacroExpander { fn expand( &self, - subtree: &Subtree, - _: Option<&Subtree>, + subtree: &TopSubtree, + _: Option<&TopSubtree>, _: &base_db::Env, _: Span, _: Span, _: Span, _: Option, - ) -> Result { + ) -> Result { let (parse, _) = syntax_bridge::token_tree_to_syntax_node( subtree, syntax_bridge::TopEntryPoint::MacroItems, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 9bd7d38f0a64..39d383f0159b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -85,6 +85,8 @@ use crate::{ FxIndexMap, LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId, }; +pub use self::path_resolution::ResolvePathResultPrefixInfo; + const PREDEFINED_TOOLS: &[SmolStr] = &[ SmolStr::new_static("clippy"), SmolStr::new_static("rustfmt"), @@ -615,13 +617,15 @@ impl DefMap { (res.resolved_def, res.segment_index) } + /// The first `Option` points at the `Enum` segment in case of `Enum::Variant`, the second + /// points at the unresolved segments. pub(crate) fn resolve_path_locally( &self, db: &dyn DefDatabase, original_module: LocalModuleId, path: &ModPath, shadow: BuiltinShadowMode, - ) -> (PerNs, Option) { + ) -> (PerNs, Option, ResolvePathResultPrefixInfo) { let res = self.resolve_path_fp_with_macro_single( db, ResolveMode::Other, @@ -630,7 +634,7 @@ impl DefMap { shadow, None, // Currently this function isn't used for macro resolution. ); - (res.resolved_def, res.segment_index) + (res.resolved_def, res.segment_index, res.prefix_info) } /// Ascends the `DefMap` hierarchy and calls `f` with every `DefMap` and containing module. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs index 747860fd8e17..d1f6ed023c2f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs @@ -110,8 +110,8 @@ pub(super) fn attr_macro_as_call_id( ) -> MacroCallId { let arg = match macro_attr.input.as_deref() { Some(AttrInput::TokenTree(tt)) => { - let mut tt = tt.as_ref().clone(); - tt.delimiter.kind = tt::DelimiterKind::Invisible; + let mut tt = tt.clone(); + tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible; Some(tt) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index f391cc41c18f..8beeda82bcaf 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -353,7 +353,7 @@ impl DefCollector<'_> { let is_cfg_enabled = item_tree .top_level_attrs(self.db, self.def_map.krate) .cfg() - .map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)); + .is_none_or(|cfg| self.cfg_options.check(&cfg) != Some(false)); if is_cfg_enabled { self.inject_prelude(); @@ -797,7 +797,7 @@ impl DefCollector<'_> { return PartialResolvedImport::Unresolved; } - if res.from_differing_crate { + if res.prefix_info.differing_crate { return PartialResolvedImport::Resolved( def.filter_visibility(|v| matches!(v, Visibility::Public)), ); @@ -1316,6 +1316,7 @@ impl DefCollector<'_> { // being cfg'ed out). // Ideally we will just expand them to nothing here. But we are only collecting macro calls, // not expanding them, so we have no way to do that. + // If you add an ignored attribute here, also add it to `Semantics::might_be_inside_macro_call()`. if matches!( def.kind, MacroDefKind::BuiltInAttr(_, expander) @@ -1451,13 +1452,7 @@ impl DefCollector<'_> { depth: usize, container: ItemContainerId, ) { - let recursion_limit = self.def_map.recursion_limit() as usize; - let recursion_limit = Limit::new(if cfg!(test) { - // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug - std::cmp::min(32, recursion_limit) - } else { - recursion_limit - }); + let recursion_limit = Limit::new(self.def_map.recursion_limit() as usize); if recursion_limit.check(depth).is_err() { cov_mark::hit!(macro_expansion_overflow); tracing::warn!("macro expansion is too deep"); @@ -2220,8 +2215,8 @@ impl ModCollector<'_, '_> { let is_export = export_attr.exists(); let local_inner = if is_export { - export_attr.tt_values().flat_map(|it| it.token_trees.iter()).any(|it| match it { - tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.sym == sym::local_inner_macros, + export_attr.tt_values().flat_map(|it| it.iter()).any(|it| match it { + tt::TtElement::Leaf(tt::Leaf::Ident(ident)) => ident.sym == sym::local_inner_macros, _ => false, }) } else { @@ -2240,7 +2235,7 @@ impl ModCollector<'_, '_> { None => { let explicit_name = attrs.by_key(&sym::rustc_builtin_macro).tt_values().next().and_then(|tt| { - match tt.token_trees.first() { + match tt.token_trees().flat_tokens().first() { Some(tt::TokenTree::Leaf(tt::Leaf::Ident(name))) => Some(name), _ => None, } @@ -2310,9 +2305,7 @@ impl ModCollector<'_, '_> { // NOTE: The item *may* have both `#[rustc_builtin_macro]` and `#[proc_macro_derive]`, // in which case rustc ignores the helper attributes from the latter, but it // "doesn't make sense in practice" (see rust-lang/rust#87027). - if let Some((name, helpers)) = - parse_macro_name_and_helper_attrs(&attr.token_trees) - { + if let Some((name, helpers)) = parse_macro_name_and_helper_attrs(attr) { // NOTE: rustc overrides the name if the macro name if it's different from the // macro name, but we assume it isn't as there's no such case yet. FIXME if // the following assertion fails. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index 8eb195680d19..47c08d3d1dc6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -43,21 +43,33 @@ pub(super) struct ResolvePathResult { pub(super) resolved_def: PerNs, pub(super) segment_index: Option, pub(super) reached_fixedpoint: ReachedFixedPoint, - pub(super) from_differing_crate: bool, + pub(super) prefix_info: ResolvePathResultPrefixInfo, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct ResolvePathResultPrefixInfo { + pub(crate) differing_crate: bool, + /// Path of the form `Enum::Variant` (and not `Variant` alone). + pub enum_variant: bool, } impl ResolvePathResult { fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult { - ResolvePathResult::new(PerNs::none(), reached_fixedpoint, None, false) + ResolvePathResult::new( + PerNs::none(), + reached_fixedpoint, + None, + ResolvePathResultPrefixInfo::default(), + ) } fn new( resolved_def: PerNs, reached_fixedpoint: ReachedFixedPoint, segment_index: Option, - from_differing_crate: bool, + prefix_info: ResolvePathResultPrefixInfo, ) -> ResolvePathResult { - ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, from_differing_crate } + ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, prefix_info } } } @@ -157,7 +169,8 @@ impl DefMap { if result.reached_fixedpoint == ReachedFixedPoint::No { result.reached_fixedpoint = new.reached_fixedpoint; } - result.from_differing_crate |= new.from_differing_crate; + result.prefix_info.differing_crate |= new.prefix_info.differing_crate; + result.prefix_info.enum_variant |= new.prefix_info.enum_variant; result.segment_index = match (result.segment_index, new.segment_index) { (Some(idx), None) => Some(idx), (Some(old), Some(new)) => Some(old.max(new)), @@ -403,14 +416,14 @@ impl DefMap { fn resolve_remaining_segments<'a>( &self, - segments: impl Iterator, + mut segments: impl Iterator, mut curr_per_ns: PerNs, path: &ModPath, db: &dyn DefDatabase, shadow: BuiltinShadowMode, original_module: LocalModuleId, ) -> ResolvePathResult { - for (i, segment) in segments { + while let Some((i, segment)) = segments.next() { let curr = match curr_per_ns.take_types_full() { Some(r) => r, None => { @@ -437,13 +450,22 @@ impl DefMap { // because `macro_use` and other preludes should be taken into account. At // this point, we know we're resolving a multi-segment path so macro kind // expectation is discarded. - let (def, s) = - defp_map.resolve_path(db, module.local_id, &path, shadow, None); + let resolution = defp_map.resolve_path_fp_with_macro( + db, + ResolveMode::Other, + module.local_id, + &path, + shadow, + None, + ); return ResolvePathResult::new( - def, + resolution.resolved_def, ReachedFixedPoint::Yes, - s.map(|s| s + i), - true, + resolution.segment_index.map(|s| s + i), + ResolvePathResultPrefixInfo { + differing_crate: true, + enum_variant: resolution.prefix_info.enum_variant, + }, ); } @@ -488,17 +510,31 @@ impl DefMap { ), }) }); - match res { - Some(res) => res, - None => { - return ResolvePathResult::new( - PerNs::types(e.into(), curr.vis, curr.import), - ReachedFixedPoint::Yes, - Some(i), - false, - ) + // FIXME: Need to filter visibility here and below? Not sure. + return match res { + Some(res) => { + if segments.next().is_some() { + // Enum variants are in value namespace, segments left => no resolution. + ResolvePathResult::empty(ReachedFixedPoint::No) + } else { + ResolvePathResult::new( + res, + ReachedFixedPoint::Yes, + None, + ResolvePathResultPrefixInfo { + enum_variant: true, + ..ResolvePathResultPrefixInfo::default() + }, + ) + } } - } + None => ResolvePathResult::new( + PerNs::types(e.into(), curr.vis, curr.import), + ReachedFixedPoint::Yes, + Some(i), + ResolvePathResultPrefixInfo::default(), + ), + }; } s => { // could be an inherent method call in UFCS form @@ -513,7 +549,7 @@ impl DefMap { PerNs::types(s, curr.vis, curr.import), ReachedFixedPoint::Yes, Some(i), - false, + ResolvePathResultPrefixInfo::default(), ); } }; @@ -522,7 +558,12 @@ impl DefMap { .filter_visibility(|vis| vis.is_visible_from_def_map(db, self, original_module)); } - ResolvePathResult::new(curr_per_ns, ReachedFixedPoint::Yes, None, false) + ResolvePathResult::new( + curr_per_ns, + ReachedFixedPoint::Yes, + None, + ResolvePathResultPrefixInfo::default(), + ) } fn resolve_name_in_module( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs index fd0b52bc7d75..b93a1c87b432 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs @@ -4,7 +4,7 @@ use hir_expand::name::{AsName, Name}; use intern::sym; use crate::attr::Attrs; -use crate::tt::{Leaf, TokenTree}; +use crate::tt::{Leaf, TokenTree, TopSubtree, TtElement}; #[derive(Debug, PartialEq, Eq)] pub struct ProcMacroDef { @@ -38,7 +38,7 @@ impl Attrs { Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr }) } else if self.by_key(&sym::proc_macro_derive).exists() { let derive = self.by_key(&sym::proc_macro_derive).tt_values().next()?; - let def = parse_macro_name_and_helper_attrs(&derive.token_trees) + let def = parse_macro_name_and_helper_attrs(derive) .map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::Derive { helpers } }); if def.is_none() { @@ -55,8 +55,8 @@ impl Attrs { // This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have // the same structure. #[rustfmt::skip] -pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)> { - match tt { +pub(crate) fn parse_macro_name_and_helper_attrs(tt: &TopSubtree) -> Option<(Name, Box<[Name]>)> { + match tt.token_trees().flat_tokens() { // `#[proc_macro_derive(Trait)]` // `#[rustc_builtin_macro(Trait)]` [TokenTree::Leaf(Leaf::Ident(trait_name))] => Some((trait_name.as_name(), Box::new([]))), @@ -67,17 +67,18 @@ pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Nam TokenTree::Leaf(Leaf::Ident(trait_name)), TokenTree::Leaf(Leaf::Punct(comma)), TokenTree::Leaf(Leaf::Ident(attributes)), - TokenTree::Subtree(helpers) + TokenTree::Subtree(_), + .. ] if comma.char == ',' && attributes.sym == sym::attributes => { + let helpers = tt::TokenTreesView::new(&tt.token_trees().flat_tokens()[3..]).try_into_subtree()?; let helpers = helpers - .token_trees .iter() .filter( - |tt| !matches!(tt, TokenTree::Leaf(Leaf::Punct(comma)) if comma.char == ','), + |tt| !matches!(tt, TtElement::Leaf(Leaf::Punct(comma)) if comma.char == ','), ) .map(|tt| match tt { - TokenTree::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()), + TtElement::Leaf(Leaf::Ident(helper)) => Some(helper.as_name()), _ => None, }) .collect::>>()?; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs index 44e132061ad4..e59c37104dd6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs @@ -240,6 +240,7 @@ pub struct PathSegment<'a> { pub args_and_bindings: Option<&'a GenericArgs>, } +#[derive(Debug, Clone, Copy)] pub struct PathSegments<'a> { segments: &'a [Name], generic_args: Option<&'a [Option]>, @@ -259,6 +260,7 @@ impl<'a> PathSegments<'a> { pub fn last(&self) -> Option> { self.get(self.len().checked_sub(1)?) } + pub fn get(&self, idx: usize) -> Option> { let res = PathSegment { name: self.segments.get(idx)?, @@ -266,24 +268,37 @@ impl<'a> PathSegments<'a> { }; Some(res) } + pub fn skip(&self, len: usize) -> PathSegments<'a> { PathSegments { segments: self.segments.get(len..).unwrap_or(&[]), generic_args: self.generic_args.and_then(|it| it.get(len..)), } } + pub fn take(&self, len: usize) -> PathSegments<'a> { PathSegments { segments: self.segments.get(..len).unwrap_or(self.segments), generic_args: self.generic_args.map(|it| it.get(..len).unwrap_or(it)), } } + pub fn strip_last(&self) -> PathSegments<'a> { PathSegments { segments: self.segments.split_last().map_or(&[], |it| it.1), generic_args: self.generic_args.map(|it| it.split_last().map_or(&[][..], |it| it.1)), } } + + pub fn strip_last_two(&self) -> PathSegments<'a> { + PathSegments { + segments: self.segments.get(..self.segments.len().saturating_sub(2)).unwrap_or(&[]), + generic_args: self + .generic_args + .map(|it| it.get(..it.len().saturating_sub(2)).unwrap_or(&[])), + } + } + pub fn iter(&self) -> impl Iterator> { self.segments .iter() diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index f4dfd42a30e9..82da57a9bb2e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -21,7 +21,7 @@ use crate::{ hir::{BindingId, ExprId, LabelId}, item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE}, lang_item::LangItemTarget, - nameres::{DefMap, MacroSubNs}, + nameres::{DefMap, MacroSubNs, ResolvePathResultPrefixInfo}, path::{ModPath, Path, PathKind}, per_ns::PerNs, type_ref::{LifetimeRef, TypesMap}, @@ -263,25 +263,37 @@ impl Resolver { &self, db: &dyn DefDatabase, path: &Path, - mut hygiene_id: HygieneId, + hygiene_id: HygieneId, ) -> Option { + self.resolve_path_in_value_ns_with_prefix_info(db, path, hygiene_id).map(|(it, _)| it) + } + + pub fn resolve_path_in_value_ns_with_prefix_info( + &self, + db: &dyn DefDatabase, + path: &Path, + mut hygiene_id: HygieneId, + ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> { let path = match path { Path::BarePath(mod_path) => mod_path, Path::Normal(it) => it.mod_path(), Path::LangItem(l, None) => { - return Some(ResolveValueResult::ValueNs( - match *l { - LangItemTarget::Function(it) => ValueNs::FunctionId(it), - LangItemTarget::Static(it) => ValueNs::StaticId(it), - LangItemTarget::Struct(it) => ValueNs::StructId(it), - LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it), - LangItemTarget::Union(_) - | LangItemTarget::ImplDef(_) - | LangItemTarget::TypeAlias(_) - | LangItemTarget::Trait(_) - | LangItemTarget::EnumId(_) => return None, - }, - None, + return Some(( + ResolveValueResult::ValueNs( + match *l { + LangItemTarget::Function(it) => ValueNs::FunctionId(it), + LangItemTarget::Static(it) => ValueNs::StaticId(it), + LangItemTarget::Struct(it) => ValueNs::StructId(it), + LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it), + LangItemTarget::Union(_) + | LangItemTarget::ImplDef(_) + | LangItemTarget::TypeAlias(_) + | LangItemTarget::Trait(_) + | LangItemTarget::EnumId(_) => return None, + }, + None, + ), + ResolvePathResultPrefixInfo::default(), )) } Path::LangItem(l, Some(_)) => { @@ -296,7 +308,10 @@ impl Resolver { | LangItemTarget::ImplDef(_) | LangItemTarget::Static(_) => return None, }; - return Some(ResolveValueResult::Partial(type_ns, 1, None)); + return Some(( + ResolveValueResult::Partial(type_ns, 1, None), + ResolvePathResultPrefixInfo::default(), + )); } }; let n_segments = path.segments().len(); @@ -326,9 +341,12 @@ impl Resolver { }); if let Some(e) = entry { - return Some(ResolveValueResult::ValueNs( - ValueNs::LocalBinding(e.binding()), - None, + return Some(( + ResolveValueResult::ValueNs( + ValueNs::LocalBinding(e.binding()), + None, + ), + ResolvePathResultPrefixInfo::default(), )); } } @@ -350,14 +368,17 @@ impl Resolver { Scope::GenericParams { params, def } => { if let Some(id) = params.find_const_by_name(first_name, *def) { let val = ValueNs::GenericParam(id); - return Some(ResolveValueResult::ValueNs(val, None)); + return Some(( + ResolveValueResult::ValueNs(val, None), + ResolvePathResultPrefixInfo::default(), + )); } } &Scope::ImplDefScope(impl_) => { if *first_name == sym::Self_.clone() { - return Some(ResolveValueResult::ValueNs( - ValueNs::ImplSelf(impl_), - None, + return Some(( + ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_), None), + ResolvePathResultPrefixInfo::default(), )); } } @@ -377,22 +398,27 @@ impl Resolver { Scope::GenericParams { params, def } => { if let Some(id) = params.find_type_by_name(first_name, *def) { let ty = TypeNs::GenericParam(id); - return Some(ResolveValueResult::Partial(ty, 1, None)); + return Some(( + ResolveValueResult::Partial(ty, 1, None), + ResolvePathResultPrefixInfo::default(), + )); } } &Scope::ImplDefScope(impl_) => { if *first_name == sym::Self_.clone() { - return Some(ResolveValueResult::Partial( - TypeNs::SelfType(impl_), - 1, - None, + return Some(( + ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1, None), + ResolvePathResultPrefixInfo::default(), )); } } Scope::AdtScope(adt) => { if *first_name == sym::Self_.clone() { let ty = TypeNs::AdtSelfType(*adt); - return Some(ResolveValueResult::Partial(ty, 1, None)); + return Some(( + ResolveValueResult::Partial(ty, 1, None), + ResolvePathResultPrefixInfo::default(), + )); } } Scope::BlockScope(m) => { @@ -413,7 +439,10 @@ impl Resolver { // `use core::u16;`. if path.kind == PathKind::Plain && n_segments > 1 { if let Some(builtin) = BuiltinType::by_name(first_name) { - return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None)); + return Some(( + ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None), + ResolvePathResultPrefixInfo::default(), + )); } } @@ -924,15 +953,15 @@ impl ModuleItemMap { &self, db: &dyn DefDatabase, path: &ModPath, - ) -> Option { - let (module_def, idx) = + ) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> { + let (module_def, unresolved_idx, prefix_info) = self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other); - match idx { + match unresolved_idx { None => { let (value, import) = to_value_ns(module_def)?; - Some(ResolveValueResult::ValueNs(value, import)) + Some((ResolveValueResult::ValueNs(value, import), prefix_info)) } - Some(idx) => { + Some(unresolved_idx) => { let def = module_def.take_types_full()?; let ty = match def.def { ModuleDefId::AdtId(it) => TypeNs::AdtId(it), @@ -948,7 +977,7 @@ impl ModuleItemMap { | ModuleDefId::MacroId(_) | ModuleDefId::StaticId(_) => return None, }; - Some(ResolveValueResult::Partial(ty, idx, def.import)) + Some((ResolveValueResult::Partial(ty, unresolved_idx, def.import), prefix_info)) } } } @@ -958,7 +987,7 @@ impl ModuleItemMap { db: &dyn DefDatabase, path: &ModPath, ) -> Option<(TypeNs, Option, Option)> { - let (module_def, idx) = + let (module_def, idx, _) = self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other); let (res, import) = to_type_ns(module_def)?; Some((res, idx, import)) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 12df3cf21882..c9c793d54f26 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -19,7 +19,7 @@ use crate::{ db::ExpandDatabase, mod_path::ModPath, span_map::SpanMapRef, - tt::{self, token_to_literal, Subtree}, + tt::{self, token_to_literal, TopSubtree}, InFile, }; @@ -107,8 +107,8 @@ impl RawAttrs { .chain(b.slice.iter().map(|it| { let mut it = it.clone(); it.id.id = (it.id.ast_index() as u32 + last_ast_index) - | (it.id.cfg_attr_index().unwrap_or(0) as u32) - << AttrId::AST_INDEX_BITS; + | ((it.id.cfg_attr_index().unwrap_or(0) as u32) + << AttrId::AST_INDEX_BITS); it })) .collect::>(); @@ -122,7 +122,7 @@ impl RawAttrs { pub fn filter(self, db: &dyn ExpandDatabase, krate: CrateId) -> RawAttrs { let has_cfg_attrs = self .iter() - .any(|attr| attr.path.as_ident().map_or(false, |name| *name == sym::cfg_attr.clone())); + .any(|attr| attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr.clone())); if !has_cfg_attrs { return self; } @@ -132,7 +132,7 @@ impl RawAttrs { self.iter() .flat_map(|attr| -> SmallVec<[_; 1]> { let is_cfg_attr = - attr.path.as_ident().map_or(false, |name| *name == sym::cfg_attr.clone()); + attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr.clone()); if !is_cfg_attr { return smallvec![attr.clone()]; } @@ -152,7 +152,7 @@ impl RawAttrs { ); let cfg_options = &crate_graph[krate].cfg_options; - let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) }; + let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg); let cfg = CfgExpr::parse(&cfg); if cfg_options.check(&cfg) == Some(false) { smallvec![] @@ -202,7 +202,7 @@ impl AttrId { } pub fn with_cfg_attr(self, idx: usize) -> AttrId { - AttrId { id: self.id | (idx as u32) << Self::AST_INDEX_BITS | Self::CFG_ATTR_SET_BITS } + AttrId { id: self.id | ((idx as u32) << Self::AST_INDEX_BITS) | Self::CFG_ATTR_SET_BITS } } } @@ -219,7 +219,7 @@ pub enum AttrInput { /// `#[attr = "string"]` Literal(tt::Literal), /// `#[attr(subtree)]` - TokenTree(Box), + TokenTree(tt::TopSubtree), } impl fmt::Display for AttrInput { @@ -254,46 +254,59 @@ impl Attr { span, DocCommentDesugarMode::ProcMacro, ); - Some(Box::new(AttrInput::TokenTree(Box::new(tree)))) + Some(Box::new(AttrInput::TokenTree(tree))) } else { None }; Some(Attr { id, path, input, ctxt: span.ctx }) } - fn from_tt(db: &dyn ExpandDatabase, mut tt: &[tt::TokenTree], id: AttrId) -> Option { - if matches!(tt, + fn from_tt( + db: &dyn ExpandDatabase, + mut tt: tt::TokenTreesView<'_>, + id: AttrId, + ) -> Option { + if matches!(tt.flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, .. })), ..] if *sym == sym::unsafe_ ) { - match tt.get(1) { - Some(tt::TokenTree::Subtree(subtree)) => tt = &subtree.token_trees, + match tt.iter().nth(1) { + Some(tt::TtElement::Subtree(_, iter)) => tt = iter.remaining(), _ => return None, } } - let first = &tt.first()?; + let first = tt.flat_tokens().first()?; let ctxt = first.first_span().ctx; - let path_end = tt - .iter() - .position(|tt| { - !matches!( + let (path, input) = { + let mut iter = tt.iter(); + let start = iter.savepoint(); + let mut input = tt::TokenTreesView::new(&[]); + let mut path = iter.from_savepoint(start); + let mut path_split_savepoint = iter.savepoint(); + while let Some(tt) = iter.next() { + path = iter.from_savepoint(start); + if !matches!( tt, - tt::TokenTree::Leaf( + tt::TtElement::Leaf( tt::Leaf::Punct(tt::Punct { char: ':' | '$', .. }) | tt::Leaf::Ident(_), ) - ) - }) - .unwrap_or(tt.len()); + ) { + input = path_split_savepoint.remaining(); + break; + } + path_split_savepoint = iter.savepoint(); + } + (path, input) + }; - let (path, input) = tt.split_at(path_end); let path = Interned::new(ModPath::from_tt(db, path)?); - let input = match input.first() { - Some(tt::TokenTree::Subtree(tree)) => { - Some(Box::new(AttrInput::TokenTree(Box::new(tree.clone())))) + let input = match (input.flat_tokens().first(), input.try_into_subtree()) { + (_, Some(tree)) => { + Some(Box::new(AttrInput::TokenTree(tt::TopSubtree::from_subtree(tree)))) } - Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))) => { - let input = match input.get(1) { + (Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))), _) => { + let input = match input.flat_tokens().get(1) { Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => { Some(Box::new(AttrInput::Literal(lit.clone()))) } @@ -352,7 +365,7 @@ impl Attr { /// #[path(ident)] pub fn single_ident_value(&self) -> Option<&tt::Ident> { match self.input.as_deref()? { - AttrInput::TokenTree(tt) => match &*tt.token_trees { + AttrInput::TokenTree(tt) => match tt.token_trees().flat_tokens() { [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] => Some(ident), _ => None, }, @@ -361,7 +374,7 @@ impl Attr { } /// #[path TokenTree] - pub fn token_tree_value(&self) -> Option<&Subtree> { + pub fn token_tree_value(&self) -> Option<&TopSubtree> { match self.input.as_deref()? { AttrInput::TokenTree(tt) => Some(tt), _ => None, @@ -375,14 +388,14 @@ impl Attr { ) -> Option + 'a> { let args = self.token_tree_value()?; - if args.delimiter.kind != DelimiterKind::Parenthesis { + if args.top_subtree().delimiter.kind != DelimiterKind::Parenthesis { return None; } let paths = args - .token_trees - .split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))) + .token_trees() + .split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))) .filter_map(move |tts| { - let span = tts.first()?.first_span(); + let span = tts.flat_tokens().first()?.first_span(); Some((ModPath::from_tt(db, tts)?, span)) }); @@ -467,11 +480,11 @@ fn inner_attributes( // Input subtree is: `(cfg, $(attr),+)` // Split it up into a `cfg` subtree and the `attr` subtrees. fn parse_cfg_attr_input( - subtree: &Subtree, -) -> Option<(&[tt::TokenTree], impl Iterator)> { + subtree: &TopSubtree, +) -> Option<(tt::TokenTreesView<'_>, impl Iterator>)> { let mut parts = subtree - .token_trees - .split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))); + .token_trees() + .split(|tt| matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))); let cfg = parts.next()?; Some((cfg, parts.filter(|it| !it.is_empty()))) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs index 74effd2fb16b..f250620e775a 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs @@ -14,7 +14,7 @@ macro_rules! register_builtin { } impl BuiltinAttrExpander { - pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult { + pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult { match *self { $( BuiltinAttrExpander::$variant => $expand, )* } @@ -36,9 +36,9 @@ impl BuiltinAttrExpander { &self, db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, - ) -> ExpandResult { + ) -> ExpandResult { self.expander()(db, id, tt, span) } @@ -75,18 +75,18 @@ pub fn find_builtin_attr(ident: &name::Name) -> Option { fn dummy_attr_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, _span: Span, -) -> ExpandResult { +) -> ExpandResult { ExpandResult::ok(tt.clone()) } fn dummy_gate_test_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { let result = quote::quote! { span=> #[cfg(test)] #tt @@ -118,47 +118,41 @@ fn dummy_gate_test_expand( fn derive_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { let loc = db.lookup_intern_macro_call(id); let derives = match &loc.kind { MacroCallKind::Attr { attr_args: Some(attr_args), .. } if loc.def.is_attribute_derive() => { attr_args } _ => { - return ExpandResult::ok(tt::Subtree::empty(tt::DelimSpan { open: span, close: span })) + return ExpandResult::ok(tt::TopSubtree::empty(tt::DelimSpan { + open: span, + close: span, + })) } }; pseudo_derive_attr_expansion(tt, derives, span) } pub fn pseudo_derive_attr_expansion( - _: &tt::Subtree, - args: &tt::Subtree, + _: &tt::TopSubtree, + args: &tt::TopSubtree, call_site: Span, -) -> ExpandResult { - let mk_leaf = |char| { - tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { - char, - spacing: tt::Spacing::Alone, - span: call_site, - })) - }; +) -> ExpandResult { + let mk_leaf = + |char| tt::Leaf::Punct(tt::Punct { char, spacing: tt::Spacing::Alone, span: call_site }); - let mut token_trees = Vec::new(); - for tt in args - .token_trees - .split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. })))) - { - token_trees.push(mk_leaf('#')); - token_trees.push(mk_leaf('!')); - token_trees.push(mk_leaf('[')); - token_trees.extend(tt.iter().cloned()); - token_trees.push(mk_leaf(']')); + let mut token_trees = tt::TopSubtreeBuilder::new(args.top_subtree().delimiter); + let iter = args.token_trees().split(|tt| { + matches!(tt, tt::TtElement::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) + }); + for tts in iter { + token_trees.extend([mk_leaf('#'), mk_leaf('!')]); + token_trees.open(tt::DelimiterKind::Bracket, call_site); + token_trees.extend_with_tt(tts); + token_trees.close(call_site); } - ExpandResult::ok(tt::Subtree { - delimiter: args.delimiter, - token_trees: token_trees.into_boxed_slice(), - }) + ExpandResult::ok(token_trees.build()) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index 7d3e8deaf08e..4510a593af4d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -1,9 +1,10 @@ //! Builtin derives. use intern::sym; -use itertools::izip; +use itertools::{izip, Itertools}; +use parser::SyntaxKind; use rustc_hash::FxHashSet; -use span::{MacroCallId, Span}; +use span::{MacroCallId, Span, SyntaxContextId}; use stdx::never; use syntax_bridge::DocCommentDesugarMode; use tracing::debug; @@ -16,8 +17,12 @@ use crate::{ span_map::ExpansionSpanMap, tt, ExpandError, ExpandResult, }; -use syntax::ast::{ - self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName, HasTypeBounds, +use syntax::{ + ast::{ + self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, FieldList, HasAttrs, + HasGenericArgs, HasGenericParams, HasModuleItem, HasName, HasTypeBounds, + }, + ted, }; macro_rules! register_builtin { @@ -28,7 +33,7 @@ macro_rules! register_builtin { } impl BuiltinDeriveExpander { - pub fn expander(&self) -> fn(Span, &tt::Subtree) -> ExpandResult { + pub fn expander(&self) -> fn(Span, &tt::TopSubtree) -> ExpandResult { match *self { $( BuiltinDeriveExpander::$trait => $expand, )* } @@ -50,9 +55,9 @@ impl BuiltinDeriveExpander { &self, db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, - ) -> ExpandResult { + ) -> ExpandResult { let span = span_with_def_site_ctxt(db, span, id); self.expander()(span, tt) } @@ -67,13 +72,15 @@ register_builtin! { Ord => ord_expand, PartialOrd => partial_ord_expand, Eq => eq_expand, - PartialEq => partial_eq_expand + PartialEq => partial_eq_expand, + CoercePointee => coerce_pointee_expand } pub fn find_builtin_derive(ident: &name::Name) -> Option { BuiltinDeriveExpander::find_by_name(ident) } +#[derive(Clone)] enum VariantShape { Struct(Vec), Tuple(usize), @@ -85,7 +92,7 @@ fn tuple_field_iterator(span: Span, n: usize) -> impl Iterator } impl VariantShape { - fn as_pattern(&self, path: tt::Subtree, span: Span) -> tt::Subtree { + fn as_pattern(&self, path: tt::TopSubtree, span: Span) -> tt::TopSubtree { self.as_pattern_map(path, span, |it| quote!(span => #it)) } @@ -99,10 +106,10 @@ impl VariantShape { fn as_pattern_map( &self, - path: tt::Subtree, + path: tt::TopSubtree, span: Span, - field_map: impl Fn(&tt::Ident) -> tt::Subtree, - ) -> tt::Subtree { + field_map: impl Fn(&tt::Ident) -> tt::TopSubtree, + ) -> tt::TopSubtree { match self { VariantShape::Struct(fields) => { let fields = fields.iter().map(|it| { @@ -147,6 +154,7 @@ impl VariantShape { } } +#[derive(Clone)] enum AdtShape { Struct(VariantShape), Enum { variants: Vec<(tt::Ident, VariantShape)>, default_variant: Option }, @@ -154,7 +162,7 @@ enum AdtShape { } impl AdtShape { - fn as_pattern(&self, span: Span, name: &tt::Ident) -> Vec { + fn as_pattern(&self, span: Span, name: &tt::Ident) -> Vec { self.as_pattern_map(name, |it| quote!(span =>#it), span) } @@ -176,9 +184,9 @@ impl AdtShape { fn as_pattern_map( &self, name: &tt::Ident, - field_map: impl Fn(&tt::Ident) -> tt::Subtree, + field_map: impl Fn(&tt::Ident) -> tt::TopSubtree, span: Span, - ) -> Vec { + ) -> Vec { match self { AdtShape::Struct(s) => { vec![s.as_pattern_map(quote! {span => #name }, span, field_map)] @@ -197,30 +205,38 @@ impl AdtShape { } } +#[derive(Clone)] struct BasicAdtInfo { name: tt::Ident, shape: AdtShape, /// first field is the name, and /// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param. /// third fields is where bounds, if any - param_types: Vec<(tt::Subtree, Option, Option)>, - where_clause: Vec, - associated_types: Vec, + param_types: Vec, + where_clause: Vec, + associated_types: Vec, } -fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result { - let (parsed, tm) = &syntax_bridge::token_tree_to_syntax_node( - tt, - syntax_bridge::TopEntryPoint::MacroItems, - parser::Edition::CURRENT_FIXME, - ); - let macro_items = ast::MacroItems::cast(parsed.syntax_node()) - .ok_or_else(|| ExpandError::other(call_site, "invalid item definition"))?; - let item = - macro_items.items().next().ok_or_else(|| ExpandError::other(call_site, "no item found"))?; - let adt = &ast::Adt::cast(item.syntax().clone()) - .ok_or_else(|| ExpandError::other(call_site, "expected struct, enum or union"))?; - let (name, generic_param_list, where_clause, shape) = match adt { +#[derive(Clone)] +struct AdtParam { + name: tt::TopSubtree, + /// `None` if this is a type parameter. + const_ty: Option, + bounds: Option, +} + +// FIXME: This whole thing needs a refactor. Each derive requires its special values, and the result is a mess. +fn parse_adt(tt: &tt::TopSubtree, call_site: Span) -> Result { + let (adt, tm) = to_adt_syntax(tt, call_site)?; + parse_adt_from_syntax(&adt, &tm, call_site) +} + +fn parse_adt_from_syntax( + adt: &ast::Adt, + tm: &span::SpanMap, + call_site: Span, +) -> Result { + let (name, generic_param_list, where_clause, shape) = match &adt { ast::Adt::Struct(it) => ( it.name(), it.generic_param_list(), @@ -276,7 +292,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result { - tt::Subtree::empty(::tt::DelimSpan { open: call_site, close: call_site }) + tt::TopSubtree::empty(::tt::DelimSpan { open: call_site, close: call_site }) } } }; @@ -291,7 +307,7 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result None, }; - let ty = if let ast::TypeOrConstParam::Const(param) = param { + let const_ty = if let ast::TypeOrConstParam::Const(param) = param { let ty = param .ty() .map(|ty| { @@ -303,13 +319,13 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result Result Result<(ast::Adt, span::SpanMap), ExpandError> { + let (parsed, tm) = syntax_bridge::token_tree_to_syntax_node( + tt, + syntax_bridge::TopEntryPoint::MacroItems, + parser::Edition::CURRENT_FIXME, + ); + let macro_items = ast::MacroItems::cast(parsed.syntax_node()) + .ok_or_else(|| ExpandError::other(call_site, "invalid item definition"))?; + let item = + macro_items.items().next().ok_or_else(|| ExpandError::other(call_site, "no item found"))?; + let adt = ast::Adt::cast(item.syntax().clone()) + .ok_or_else(|| ExpandError::other(call_site, "expected struct, enum or union"))?; + Ok((adt, tm)) +} + fn name_to_token( call_site: Span, token_map: &ExpansionSpanMap, @@ -413,59 +447,85 @@ fn name_to_token( /// therefore does not get bound by the derived trait. fn expand_simple_derive( invoc_span: Span, - tt: &tt::Subtree, - trait_path: tt::Subtree, - make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree, -) -> ExpandResult { + tt: &tt::TopSubtree, + trait_path: tt::TopSubtree, + make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::TopSubtree, +) -> ExpandResult { let info = match parse_adt(tt, invoc_span) { Ok(info) => info, Err(e) => { return ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: invoc_span, close: invoc_span }), + tt::TopSubtree::empty(tt::DelimSpan { open: invoc_span, close: invoc_span }), e, ) } }; + ExpandResult::ok(expand_simple_derive_with_parsed( + invoc_span, + info, + trait_path, + make_trait_body, + true, + tt::TopSubtree::empty(tt::DelimSpan::from_single(invoc_span)), + )) +} + +fn expand_simple_derive_with_parsed( + invoc_span: Span, + info: BasicAdtInfo, + trait_path: tt::TopSubtree, + make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::TopSubtree, + constrain_to_trait: bool, + extra_impl_params: tt::TopSubtree, +) -> tt::TopSubtree { let trait_body = make_trait_body(&info); let mut where_block: Vec<_> = info.where_clause.into_iter().map(|w| quote! {invoc_span => #w , }).collect(); let (params, args): (Vec<_>, Vec<_>) = info .param_types .into_iter() - .map(|(ident, param_ty, bound)| { - let ident_ = ident.clone(); - if let Some(b) = bound { - let ident = ident.clone(); - where_block.push(quote! {invoc_span => #ident : #b , }); + .map(|param| { + let ident = param.name; + if let Some(b) = param.bounds { + let ident2 = ident.clone(); + where_block.push(quote! {invoc_span => #ident2 : #b , }); } - if let Some(ty) = param_ty { - (quote! {invoc_span => const #ident : #ty , }, quote! {invoc_span => #ident_ , }) + if let Some(ty) = param.const_ty { + let ident2 = ident.clone(); + (quote! {invoc_span => const #ident : #ty , }, quote! {invoc_span => #ident2 , }) } else { let bound = trait_path.clone(); - (quote! {invoc_span => #ident : #bound , }, quote! {invoc_span => #ident_ , }) + let ident2 = ident.clone(); + let param = if constrain_to_trait { + quote! {invoc_span => #ident : #bound , } + } else { + quote! {invoc_span => #ident , } + }; + (param, quote! {invoc_span => #ident2 , }) } }) .unzip(); - where_block.extend(info.associated_types.iter().map(|it| { - let it = it.clone(); - let bound = trait_path.clone(); - quote! {invoc_span => #it : #bound , } - })); + if constrain_to_trait { + where_block.extend(info.associated_types.iter().map(|it| { + let it = it.clone(); + let bound = trait_path.clone(); + quote! {invoc_span => #it : #bound , } + })); + } let name = info.name; - let expanded = quote! {invoc_span => - impl < ##params > #trait_path for #name < ##args > where ##where_block { #trait_body } - }; - ExpandResult::ok(expanded) + quote! {invoc_span => + impl < ##params #extra_impl_params > #trait_path for #name < ##args > where ##where_block { #trait_body } + } } -fn copy_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { +fn copy_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { let krate = dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::marker::Copy }, |_| quote! {span =>}) } -fn clone_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { +fn clone_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { let krate = dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::clone::Clone }, |adt| { if matches!(adt.shape, AdtShape::Union) { @@ -505,18 +565,18 @@ fn clone_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { } /// This function exists since `quote! {span => => }` doesn't work. -fn fat_arrow(span: Span) -> tt::Subtree { +fn fat_arrow(span: Span) -> tt::TopSubtree { let eq = tt::Punct { char: '=', spacing: ::tt::Spacing::Joint, span }; quote! {span => #eq> } } /// This function exists since `quote! {span => && }` doesn't work. -fn and_and(span: Span) -> tt::Subtree { +fn and_and(span: Span) -> tt::TopSubtree { let and = tt::Punct { char: '&', spacing: ::tt::Spacing::Joint, span }; quote! {span => #and& } } -fn default_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { +fn default_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { let krate = &dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::default::Default }, |adt| { let body = match &adt.shape { @@ -555,7 +615,7 @@ fn default_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { }) } -fn debug_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { +fn debug_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { let krate = &dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::fmt::Debug }, |adt| { let for_variant = |name: String, v: &VariantShape| match v { @@ -627,7 +687,7 @@ fn debug_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { }) } -fn hash_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { +fn hash_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { let krate = &dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::hash::Hash }, |adt| { if matches!(adt.shape, AdtShape::Union) { @@ -674,12 +734,12 @@ fn hash_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { }) } -fn eq_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { +fn eq_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { let krate = dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>}) } -fn partial_eq_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { +fn partial_eq_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { let krate = dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::cmp::PartialEq }, |adt| { if matches!(adt.shape, AdtShape::Union) { @@ -731,7 +791,7 @@ fn self_and_other_patterns( adt: &BasicAdtInfo, name: &tt::Ident, span: Span, -) -> (Vec, Vec) { +) -> (Vec, Vec) { let self_patterns = adt.shape.as_pattern_map( name, |it| { @@ -751,16 +811,16 @@ fn self_and_other_patterns( (self_patterns, other_patterns) } -fn ord_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { +fn ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { let krate = &dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::cmp::Ord }, |adt| { fn compare( krate: &tt::Ident, - left: tt::Subtree, - right: tt::Subtree, - rest: tt::Subtree, + left: tt::TopSubtree, + right: tt::TopSubtree, + rest: tt::TopSubtree, span: Span, - ) -> tt::Subtree { + ) -> tt::TopSubtree { let fat_arrow1 = fat_arrow(span); let fat_arrow2 = fat_arrow(span); quote! {span => @@ -809,16 +869,16 @@ fn ord_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { }) } -fn partial_ord_expand(span: Span, tt: &tt::Subtree) -> ExpandResult { +fn partial_ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { let krate = &dollar_crate(span); expand_simple_derive(span, tt, quote! {span => #krate::cmp::PartialOrd }, |adt| { fn compare( krate: &tt::Ident, - left: tt::Subtree, - right: tt::Subtree, - rest: tt::Subtree, + left: tt::TopSubtree, + right: tt::TopSubtree, + rest: tt::TopSubtree, span: Span, - ) -> tt::Subtree { + ) -> tt::TopSubtree { let fat_arrow1 = fat_arrow(span); let fat_arrow2 = fat_arrow(span); quote! {span => @@ -871,3 +931,493 @@ fn partial_ord_expand(span: Span, tt: &tt::Subtree) -> ExpandResult } }) } + +fn coerce_pointee_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult { + let (adt, _span_map) = match to_adt_syntax(tt, span) { + Ok(it) => it, + Err(err) => { + return ExpandResult::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), err); + } + }; + let adt = adt.clone_for_update(); + let ast::Adt::Struct(strukt) = &adt else { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other(span, "`CoercePointee` can only be derived on `struct`s"), + ); + }; + let has_at_least_one_field = strukt + .field_list() + .map(|it| match it { + ast::FieldList::RecordFieldList(it) => it.fields().next().is_some(), + ast::FieldList::TupleFieldList(it) => it.fields().next().is_some(), + }) + .unwrap_or(false); + if !has_at_least_one_field { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other( + span, + "`CoercePointee` can only be derived on `struct`s with at least one field", + ), + ); + } + let is_repr_transparent = strukt.attrs().any(|attr| { + attr.as_simple_call().is_some_and(|(name, tt)| { + name == "repr" + && tt.syntax().children_with_tokens().any(|it| { + it.into_token().is_some_and(|it| { + it.kind() == SyntaxKind::IDENT && it.text() == "transparent" + }) + }) + }) + }); + if !is_repr_transparent { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other( + span, + "`CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`", + ), + ); + } + let type_params = strukt + .generic_param_list() + .into_iter() + .flat_map(|generics| { + generics.generic_params().filter_map(|param| match param { + ast::GenericParam::TypeParam(param) => Some(param), + _ => None, + }) + }) + .collect_vec(); + if type_params.is_empty() { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other( + span, + "`CoercePointee` can only be derived on `struct`s that are generic over at least one type", + ), + ); + } + let (pointee_param, pointee_param_idx) = if type_params.len() == 1 { + // Regardless of the only type param being designed as `#[pointee]` or not, we can just use it as such. + (type_params[0].clone(), 0) + } else { + let mut pointees = type_params.iter().cloned().enumerate().filter(|(_, param)| { + param.attrs().any(|attr| { + let is_pointee = attr.as_simple_atom().is_some_and(|name| name == "pointee"); + if is_pointee { + // Remove the `#[pointee]` attribute so it won't be present in the generated + // impls (where we cannot resolve it). + ted::remove(attr.syntax()); + } + is_pointee + }) + }); + match (pointees.next(), pointees.next()) { + (Some((pointee_idx, pointee)), None) => (pointee, pointee_idx), + (None, _) => { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other( + span, + "exactly one generic type parameter must be marked \ + as `#[pointee]` to derive `CoercePointee` traits", + ), + ) + } + (Some(_), Some(_)) => { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other( + span, + "only one type parameter can be marked as `#[pointee]` \ + when deriving `CoercePointee` traits", + ), + ) + } + } + }; + let (Some(struct_name), Some(pointee_param_name)) = (strukt.name(), pointee_param.name()) + else { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other(span, "invalid item"), + ); + }; + + { + let mut pointee_has_maybe_sized_bound = false; + if let Some(bounds) = pointee_param.type_bound_list() { + pointee_has_maybe_sized_bound |= bounds.bounds().any(is_maybe_sized_bound); + } + if let Some(where_clause) = strukt.where_clause() { + pointee_has_maybe_sized_bound |= where_clause.predicates().any(|pred| { + let Some(ast::Type::PathType(ty)) = pred.ty() else { return false }; + let is_not_pointee = ty.path().is_none_or(|path| { + let is_pointee = path + .as_single_name_ref() + .is_some_and(|name| name.text() == pointee_param_name.text()); + !is_pointee + }); + if is_not_pointee { + return false; + } + pred.type_bound_list() + .is_some_and(|bounds| bounds.bounds().any(is_maybe_sized_bound)) + }) + } + if !pointee_has_maybe_sized_bound { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other( + span, + format!("`derive(CoercePointee)` requires `{pointee_param_name}` to be marked `?Sized`"), + ), + ); + } + } + + const ADDED_PARAM: &str = "__S"; + + let where_clause = strukt.get_or_create_where_clause(); + + { + let mut new_predicates = Vec::new(); + + // # Rewrite generic parameter bounds + // For each bound `U: ..` in `struct`, make a new bound with `__S` in place of `#[pointee]` + // Example: + // ``` + // struct< + // U: Trait, + // #[pointee] T: Trait + ?Sized, + // V: Trait> ... + // ``` + // ... generates this `impl` generic parameters + // ``` + // impl< + // U: Trait, + // T: Trait + ?Sized, + // V: Trait + // > + // where + // U: Trait<__S>, + // __S: Trait<__S> + ?Sized, + // V: Trait<__S> ... + // ``` + for param in &type_params { + let Some(param_name) = param.name() else { continue }; + if let Some(bounds) = param.type_bound_list() { + // If the target type is the pointee, duplicate the bound as whole. + // Otherwise, duplicate only bounds that mention the pointee. + let is_pointee = param_name.text() == pointee_param_name.text(); + let new_bounds = bounds + .bounds() + .map(|bound| bound.clone_subtree().clone_for_update()) + .filter(|bound| { + bound.ty().is_some_and(|ty| { + substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM) + || is_pointee + }) + }); + let new_bounds_target = if is_pointee { + make::name_ref(ADDED_PARAM) + } else { + make::name_ref(¶m_name.text()) + }; + new_predicates.push( + make::where_pred( + make::ty_path(make::path_from_segments( + [make::path_segment(new_bounds_target)], + false, + )), + new_bounds, + ) + .clone_for_update(), + ); + } + } + + // # Rewrite `where` clauses + // + // Move on to `where` clauses. + // Example: + // ``` + // struct MyPointer<#[pointee] T, ..> + // where + // U: Trait + Trait, + // Companion: Trait, + // T: Trait + ?Sized, + // { .. } + // ``` + // ... will have a impl prelude like so + // ``` + // impl<..> .. + // where + // U: Trait + Trait, + // U: Trait<__S>, + // Companion: Trait, + // Companion<__S>: Trait<__S>, + // T: Trait + ?Sized, + // __S: Trait<__S> + ?Sized, + // ``` + // + // We should also write a few new `where` bounds from `#[pointee] T` to `__S` + // as well as any bound that indirectly involves the `#[pointee] T` type. + for predicate in where_clause.predicates() { + let predicate = predicate.clone_subtree().clone_for_update(); + let Some(pred_target) = predicate.ty() else { continue }; + + // If the target type references the pointee, duplicate the bound as whole. + // Otherwise, duplicate only bounds that mention the pointee. + if substitute_type_in_bound( + pred_target.clone(), + &pointee_param_name.text(), + ADDED_PARAM, + ) { + if let Some(bounds) = predicate.type_bound_list() { + for bound in bounds.bounds() { + if let Some(ty) = bound.ty() { + substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM); + } + } + } + + new_predicates.push(predicate); + } else if let Some(bounds) = predicate.type_bound_list() { + let new_bounds = bounds + .bounds() + .map(|bound| bound.clone_subtree().clone_for_update()) + .filter(|bound| { + bound.ty().is_some_and(|ty| { + substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM) + }) + }); + new_predicates.push(make::where_pred(pred_target, new_bounds).clone_for_update()); + } + } + + for new_predicate in new_predicates { + where_clause.add_predicate(new_predicate); + } + } + + { + // # Add `Unsize<__S>` bound to `#[pointee]` at the generic parameter location + // + // Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it. + where_clause.add_predicate( + make::where_pred( + make::ty_path(make::path_from_segments( + [make::path_segment(make::name_ref(&pointee_param_name.text()))], + false, + )), + [make::type_bound(make::ty_path(make::path_from_segments( + [ + make::path_segment(make::name_ref("core")), + make::path_segment(make::name_ref("marker")), + make::generic_ty_path_segment( + make::name_ref("Unsize"), + [make::type_arg(make::ty_path(make::path_from_segments( + [make::path_segment(make::name_ref(ADDED_PARAM))], + false, + ))) + .into()], + ), + ], + true, + )))], + ) + .clone_for_update(), + ); + } + + let self_for_traits = { + // Replace the `#[pointee]` with `__S`. + let mut type_param_idx = 0; + let self_params_for_traits = strukt + .generic_param_list() + .into_iter() + .flat_map(|params| params.generic_params()) + .filter_map(|param| { + Some(match param { + ast::GenericParam::ConstParam(param) => { + ast::GenericArg::ConstArg(make::expr_const_value(¶m.name()?.text())) + } + ast::GenericParam::LifetimeParam(param) => { + make::lifetime_arg(param.lifetime()?).into() + } + ast::GenericParam::TypeParam(param) => { + let name = if pointee_param_idx == type_param_idx { + make::name_ref(ADDED_PARAM) + } else { + make::name_ref(¶m.name()?.text()) + }; + type_param_idx += 1; + make::type_arg(make::ty_path(make::path_from_segments( + [make::path_segment(name)], + false, + ))) + .into() + } + }) + }); + let self_for_traits = make::path_from_segments( + [make::generic_ty_path_segment( + make::name_ref(&struct_name.text()), + self_params_for_traits, + )], + false, + ) + .clone_for_update(); + self_for_traits + }; + + let mut span_map = span::SpanMap::empty(); + // One span for them all. + span_map.push(adt.syntax().text_range().end(), span); + + let self_for_traits = syntax_bridge::syntax_node_to_token_tree( + self_for_traits.syntax(), + &span_map, + span, + DocCommentDesugarMode::ProcMacro, + ); + let info = match parse_adt_from_syntax(&adt, &span_map, span) { + Ok(it) => it, + Err(err) => { + return ExpandResult::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), err) + } + }; + + let self_for_traits2 = self_for_traits.clone(); + let krate = dollar_crate(span); + let krate2 = krate.clone(); + let dispatch_from_dyn = expand_simple_derive_with_parsed( + span, + info.clone(), + quote! {span => #krate2::ops::DispatchFromDyn<#self_for_traits2> }, + |_adt| quote! {span => }, + false, + quote! {span => __S }, + ); + let coerce_unsized = expand_simple_derive_with_parsed( + span, + info, + quote! {span => #krate::ops::CoerceUnsized<#self_for_traits> }, + |_adt| quote! {span => }, + false, + quote! {span => __S }, + ); + return ExpandResult::ok(quote! {span => #dispatch_from_dyn #coerce_unsized }); + + fn is_maybe_sized_bound(bound: ast::TypeBound) -> bool { + if bound.question_mark_token().is_none() { + return false; + } + let Some(ast::Type::PathType(ty)) = bound.ty() else { + return false; + }; + let Some(path) = ty.path() else { + return false; + }; + return segments_eq(&path, &["Sized"]) + || segments_eq(&path, &["core", "marker", "Sized"]) + || segments_eq(&path, &["std", "marker", "Sized"]); + + fn segments_eq(path: &ast::Path, expected: &[&str]) -> bool { + path.segments().zip_longest(expected.iter().copied()).all(|value| { + value.both().is_some_and(|(segment, expected)| { + segment.name_ref().is_some_and(|name| name.text() == expected) + }) + }) + } + } + + /// Returns true if any substitution was performed. + fn substitute_type_in_bound(ty: ast::Type, param_name: &str, replacement: &str) -> bool { + return match ty { + ast::Type::ArrayType(ty) => { + ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::DynTraitType(ty) => go_bounds(ty.type_bound_list(), param_name, replacement), + ast::Type::FnPtrType(ty) => any_long( + ty.param_list() + .into_iter() + .flat_map(|params| params.params().filter_map(|param| param.ty())) + .chain(ty.ret_type().and_then(|it| it.ty())), + |ty| substitute_type_in_bound(ty, param_name, replacement), + ), + ast::Type::ForType(ty) => { + ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::ImplTraitType(ty) => { + go_bounds(ty.type_bound_list(), param_name, replacement) + } + ast::Type::ParenType(ty) => { + ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::PathType(ty) => ty.path().is_some_and(|path| { + if path.as_single_name_ref().is_some_and(|name| name.text() == param_name) { + ted::replace( + path.syntax(), + make::path_from_segments( + [make::path_segment(make::name_ref(replacement))], + false, + ) + .clone_for_update() + .syntax(), + ); + return true; + } + + any_long( + path.segments() + .filter_map(|segment| segment.generic_arg_list()) + .flat_map(|it| it.generic_args()) + .filter_map(|generic_arg| match generic_arg { + ast::GenericArg::TypeArg(ty) => ty.ty(), + _ => None, + }), + |ty| substitute_type_in_bound(ty, param_name, replacement), + ) + }), + ast::Type::PtrType(ty) => { + ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::RefType(ty) => { + ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::SliceType(ty) => { + ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::TupleType(ty) => { + any_long(ty.fields(), |ty| substitute_type_in_bound(ty, param_name, replacement)) + } + ast::Type::InferType(_) | ast::Type::MacroType(_) | ast::Type::NeverType(_) => false, + }; + + fn go_bounds( + bounds: Option, + param_name: &str, + replacement: &str, + ) -> bool { + bounds.is_some_and(|bounds| { + any_long(bounds.bounds(), |bound| { + bound + .ty() + .is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + }) + }) + } + + /// Like [`Iterator::any()`], but not short-circuiting. + fn any_long bool>(iter: I, mut f: F) -> bool { + let mut result = false; + iter.for_each(|item| result |= f(item)); + result + } + } +} 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 b76db2e0052b..5b06de98757f 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 @@ -14,12 +14,12 @@ use syntax::{ use syntax_bridge::syntax_node_to_token_tree; use crate::{ - builtin::quote::{dollar_crate, quote}, + builtin::quote::{dollar_crate, quote, WithDelimiter}, db::ExpandDatabase, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt}, name, span_map::SpanMap, - tt::{self, DelimSpan}, + tt::{self, DelimSpan, TtElement, TtIter}, ExpandError, ExpandResult, HirFileIdExt, Lookup as _, MacroCallId, }; @@ -36,7 +36,7 @@ macro_rules! register_builtin { } impl BuiltinFnLikeExpander { - fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult { + fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult { match *self { $( BuiltinFnLikeExpander::$kind => $expand, )* } @@ -44,7 +44,7 @@ macro_rules! register_builtin { } impl EagerExpander { - fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult { + fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::TopSubtree, Span) -> ExpandResult { match *self { $( EagerExpander::$e_kind => $e_expand, )* } @@ -66,9 +66,9 @@ impl BuiltinFnLikeExpander { &self, db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, - ) -> ExpandResult { + ) -> ExpandResult { let span = span_with_def_site_ctxt(db, span, id); self.expander()(db, id, tt, span) } @@ -83,9 +83,9 @@ impl EagerExpander { &self, db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, - ) -> ExpandResult { + ) -> ExpandResult { let span = span_with_def_site_ctxt(db, span, id); self.expander()(db, id, tt, span) } @@ -146,24 +146,16 @@ register_builtin! { (option_env, OptionEnv) => option_env_expand } -fn mk_pound(span: Span) -> tt::Subtree { - crate::builtin::quote::IntoTt::to_subtree( - vec![crate::tt::Leaf::Punct(crate::tt::Punct { - char: '#', - spacing: crate::tt::Spacing::Alone, - span, - }) - .into()], - span, - ) +fn mk_pound(span: Span) -> tt::Leaf { + crate::tt::Leaf::Punct(crate::tt::Punct { char: '#', spacing: crate::tt::Spacing::Alone, span }) } fn module_path_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { // Just return a dummy result. ExpandResult::ok(quote! {span => "module::path" @@ -173,48 +165,48 @@ fn module_path_expand( fn line_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { // dummy implementation for type-checking purposes // Note that `line!` and `column!` will never be implemented properly, as they are by definition // not incremental - ExpandResult::ok(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(span), - token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + ExpandResult::ok(tt::TopSubtree::invisible_from_leaves( + span, + [tt::Leaf::Literal(tt::Literal { symbol: sym::INTEGER_0.clone(), span, kind: tt::LitKind::Integer, suffix: Some(sym::u32.clone()), - }))]), - }) + })], + )) } fn log_syntax_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { ExpandResult::ok(quote! {span =>}) } fn trace_macros_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { ExpandResult::ok(quote! {span =>}) } fn stringify_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { - let pretty = ::tt::pretty(&tt.token_trees); +) -> ExpandResult { + let pretty = ::tt::pretty(tt.token_trees().flat_tokens()); let expanded = quote! {span => #pretty @@ -226,39 +218,35 @@ fn stringify_expand( fn assert_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { let call_site_span = span_with_call_site_ctxt(db, span, id); - let mut iter = ::tt::iter::TtIter::new(tt); + let mut iter = tt.iter(); let cond = expect_fragment( &mut iter, parser::PrefixEntryPoint::Expr, db.crate_graph()[id.lookup(db).krate].edition, - tt::DelimSpan { open: tt.delimiter.open, close: tt.delimiter.close }, + tt.top_subtree().delimiter.delim_span(), ); _ = iter.expect_char(','); - let rest = iter.as_slice(); + let rest = iter.remaining(); let dollar_crate = dollar_crate(span); - let expanded = match cond.value { - Some(cond) => { - let panic_args = rest.iter().cloned(); - let mac = if use_panic_2021(db, span) { - quote! {call_site_span => #dollar_crate::panic::panic_2021!(##panic_args) } - } else { - quote! {call_site_span => #dollar_crate::panic!(##panic_args) } - }; - quote! {call_site_span =>{ - if !(#cond) { - #mac; - } - }} - } - None => quote! {call_site_span =>{}}, + let panic_args = rest.iter(); + let mac = if use_panic_2021(db, span) { + quote! {call_site_span => #dollar_crate::panic::panic_2021!(##panic_args) } + } else { + quote! {call_site_span => #dollar_crate::panic!(##panic_args) } }; + let value = cond.value; + let expanded = quote! {call_site_span =>{ + if !(#value) { + #mac; + } + }}; match cond.err { Some(err) => ExpandResult::new(expanded, err.into()), @@ -269,9 +257,9 @@ fn assert_expand( fn file_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { // FIXME: RA purposefully lacks knowledge of absolute file names // so just return "". let file_name = "file"; @@ -286,12 +274,12 @@ fn file_expand( fn format_args_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { let pound = mk_pound(span); let mut tt = tt.clone(); - tt.delimiter.kind = tt::DelimiterKind::Parenthesis; + tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis; ExpandResult::ok(quote! {span => builtin #pound format_args #tt }) @@ -300,17 +288,17 @@ fn format_args_expand( fn format_args_nl_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { let pound = mk_pound(span); let mut tt = tt.clone(); - tt.delimiter.kind = tt::DelimiterKind::Parenthesis; + tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis; if let Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, kind: tt::LitKind::Str, .. - }))) = tt.token_trees.first_mut() + }))) = tt.0.get_mut(1) { *text = Symbol::intern(&format_smolstr!("{}\\n", text.as_str())); } @@ -322,11 +310,11 @@ fn format_args_nl_expand( fn asm_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { let mut tt = tt.clone(); - tt.delimiter.kind = tt::DelimiterKind::Parenthesis; + tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis; let pound = mk_pound(span); let expanded = quote! {span => builtin #pound asm #tt @@ -337,9 +325,9 @@ fn asm_expand( fn cfg_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { let loc = db.lookup_intern_macro_call(id); let expr = CfgExpr::parse(tt); let enabled = db.crate_graph()[loc.krate].cfg_options.check(&expr) != Some(false); @@ -350,9 +338,9 @@ fn cfg_expand( fn panic_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { let dollar_crate = dollar_crate(span); let call_site_span = span_with_call_site_ctxt(db, span, id); @@ -362,19 +350,18 @@ fn panic_expand( sym::panic_2015.clone() }; - // Expand to a macro call `$crate::panic::panic_{edition}` - let mut call = quote!(call_site_span =>#dollar_crate::panic::#mac!); - // Pass the original arguments - let mut subtree = tt.clone(); - subtree.delimiter = tt::Delimiter { - open: call_site_span, - close: call_site_span, - kind: tt::DelimiterKind::Parenthesis, + let subtree = WithDelimiter { + delimiter: tt::Delimiter { + open: call_site_span, + close: call_site_span, + kind: tt::DelimiterKind::Parenthesis, + }, + token_trees: tt.token_trees(), }; - // FIXME(slow): quote! have a way to expand to builder to make this a vec! - call.push(tt::TokenTree::Subtree(subtree)); + // Expand to a macro call `$crate::panic::panic_{edition}` + let call = quote!(call_site_span =>#dollar_crate::panic::#mac! #subtree); ExpandResult::ok(call) } @@ -382,9 +369,9 @@ fn panic_expand( fn unreachable_expand( db: &dyn ExpandDatabase, id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { let dollar_crate = dollar_crate(span); let call_site_span = span_with_call_site_ctxt(db, span, id); @@ -394,19 +381,16 @@ fn unreachable_expand( sym::unreachable_2015.clone() }; - // Expand to a macro call `$crate::panic::panic_{edition}` - let mut call = quote!(call_site_span =>#dollar_crate::panic::#mac!); - // Pass the original arguments let mut subtree = tt.clone(); - subtree.delimiter = tt::Delimiter { + *subtree.top_subtree_delimiter_mut() = tt::Delimiter { open: call_site_span, close: call_site_span, kind: tt::DelimiterKind::Parenthesis, }; - // FIXME(slow): quote! have a way to expand to builder to make this a vec! - call.push(tt::TokenTree::Subtree(subtree)); + // Expand to a macro call `$crate::panic::panic_{edition}` + let call = quote!(call_site_span =>#dollar_crate::panic::#mac! #subtree); ExpandResult::ok(call) } @@ -436,11 +420,11 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool { fn compile_error_expand( _db: &dyn ExpandDatabase, _id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { - let err = match &*tt.token_trees { - [tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { +) -> ExpandResult { + let err = match &*tt.0 { + [_, tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span: _, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_), @@ -455,9 +439,9 @@ fn compile_error_expand( fn concat_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, call_site: Span, -) -> ExpandResult { +) -> ExpandResult { let mut err = None; let mut text = String::new(); let mut span: Option = None; @@ -466,19 +450,19 @@ fn concat_expand( Some(_) => (), None => span = Some(s), }; - for (i, mut t) in tt.token_trees.iter().enumerate() { + for (i, mut t) in tt.iter().enumerate() { // FIXME: hack on top of a hack: `$e:expr` captures get surrounded in parentheses // to ensure the right parsing order, so skip the parentheses here. Ideally we'd // implement rustc's model. cc https://github.com/rust-lang/rust-analyzer/pull/10623 - if let tt::TokenTree::Subtree(tt::Subtree { delimiter: delim, token_trees }) = t { - if let [tt] = &**token_trees { - if delim.kind == tt::DelimiterKind::Parenthesis { - t = tt; + if let TtElement::Subtree(subtree, subtree_iter) = &t { + if let [tt::TokenTree::Leaf(tt)] = subtree_iter.remaining().flat_tokens() { + if subtree.delimiter.kind == tt::DelimiterKind::Parenthesis { + t = TtElement::Leaf(tt); } } } match t { - tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => { + TtElement::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => { // concat works with string and char literals, so remove any quotes. // It also works with integer, float and boolean literals, so just use the rest // as-is. @@ -511,28 +495,28 @@ fn concat_expand( } } // handle boolean literals - tt::TokenTree::Leaf(tt::Leaf::Ident(id)) + TtElement::Leaf(tt::Leaf::Ident(id)) if i % 2 == 0 && (id.sym == sym::true_ || id.sym == sym::false_) => { text.push_str(id.sym.as_str()); record_span(id.span); } - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), + TtElement::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), _ => { err.get_or_insert(ExpandError::other(call_site, "unexpected token")); } } } - let span = span.unwrap_or(tt.delimiter.open); + let span = span.unwrap_or_else(|| tt.top_subtree().delimiter.open); ExpandResult { value: quote!(span =>#text), err } } fn concat_bytes_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, call_site: Span, -) -> ExpandResult { +) -> ExpandResult { let mut bytes = String::new(); let mut err = None; let mut span: Option = None; @@ -541,9 +525,9 @@ fn concat_bytes_expand( Some(_) => (), None => span = Some(s), }; - for (i, t) in tt.token_trees.iter().enumerate() { + for (i, t) in tt.iter().enumerate() { match t { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind, @@ -570,10 +554,12 @@ fn concat_bytes_expand( } } } - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), - tt::TokenTree::Subtree(tree) if tree.delimiter.kind == tt::DelimiterKind::Bracket => { + TtElement::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), + TtElement::Subtree(tree, tree_iter) + if tree.delimiter.kind == tt::DelimiterKind::Bracket => + { if let Err(e) = - concat_bytes_expand_subtree(tree, &mut bytes, &mut record_span, call_site) + concat_bytes_expand_subtree(tree_iter, &mut bytes, &mut record_span, call_site) { err.get_or_insert(e); break; @@ -585,31 +571,30 @@ fn concat_bytes_expand( } } } - let span = span.unwrap_or(tt.delimiter.open); + let span = span.unwrap_or(tt.top_subtree().delimiter.open); ExpandResult { - value: tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(span), - token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + value: tt::TopSubtree::invisible_from_leaves( + span, + [tt::Leaf::Literal(tt::Literal { symbol: Symbol::intern(&bytes), span, kind: tt::LitKind::ByteStr, suffix: None, - }))] - .into(), - }, + })], + ), err, } } fn concat_bytes_expand_subtree( - tree: &tt::Subtree, + tree_iter: TtIter<'_>, bytes: &mut String, mut record_span: impl FnMut(Span), err_span: Span, ) -> Result<(), ExpandError> { - for (ti, tt) in tree.token_trees.iter().enumerate() { + for (ti, tt) in tree_iter.enumerate() { match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind: tt::LitKind::Byte, @@ -620,7 +605,7 @@ fn concat_bytes_expand_subtree( } record_span(*span); } - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind: tt::LitKind::Integer, @@ -631,7 +616,7 @@ fn concat_bytes_expand_subtree( bytes.extend(b.escape_ascii().filter_map(|it| char::from_u32(it as u32))); } } - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if ti % 2 == 1 && punct.char == ',' => (), + TtElement::Leaf(tt::Leaf::Punct(punct)) if ti % 2 == 1 && punct.char == ',' => (), _ => { return Err(ExpandError::other(err_span, "unexpected token")); } @@ -643,17 +628,17 @@ fn concat_bytes_expand_subtree( fn concat_idents_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { let mut err = None; let mut ident = String::new(); - for (i, t) in tt.token_trees.iter().enumerate() { + for (i, t) in tt.iter().enumerate() { match t { - tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => { + TtElement::Leaf(tt::Leaf::Ident(id)) => { ident.push_str(id.sym.as_str()); } - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), + TtElement::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), _ => { err.get_or_insert(ExpandError::other(span, "unexpected token")); } @@ -685,18 +670,19 @@ fn relative_file( } } -fn parse_string(tt: &tt::Subtree) -> Result<(Symbol, Span), ExpandError> { - tt.token_trees - .first() - .ok_or(tt.delimiter.open.cover(tt.delimiter.close)) +fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> { + let delimiter = tt.top_subtree().delimiter; + tt.iter() + .next() + .ok_or(delimiter.open.cover(delimiter.close)) .and_then(|tt| match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind: tt::LitKind::Str, suffix: _, })) => Ok((unescape_str(text), *span)), - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind: tt::LitKind::StrRaw(_), @@ -705,26 +691,30 @@ fn parse_string(tt: &tt::Subtree) -> Result<(Symbol, Span), ExpandError> { // FIXME: We wrap expression fragments in parentheses which can break this expectation // here // Remove this once we handle none delims correctly - tt::TokenTree::Subtree(tt) if tt.delimiter.kind == DelimiterKind::Parenthesis => { - tt.token_trees.first().and_then(|tt| match tt { - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - symbol: text, - span, - kind: tt::LitKind::Str, - suffix: _, - })) => Some((unescape_str(text), *span)), - tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - symbol: text, - span, - kind: tt::LitKind::StrRaw(_), - suffix: _, - })) => Some((text.clone(), *span)), - _ => None, - }) + TtElement::Subtree(tt, mut tt_iter) + if tt.delimiter.kind == DelimiterKind::Parenthesis => + { + tt_iter + .next() + .and_then(|tt| match tt { + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { + symbol: text, + span, + kind: tt::LitKind::Str, + suffix: _, + })) => Some((unescape_str(text), *span)), + TtElement::Leaf(tt::Leaf::Literal(tt::Literal { + symbol: text, + span, + kind: tt::LitKind::StrRaw(_), + suffix: _, + })) => Some((text.clone(), *span)), + _ => None, + }) + .ok_or(delimiter.open.cover(delimiter.close)) } - .ok_or(tt.delimiter.open.cover(tt.delimiter.close)), - ::tt::TokenTree::Leaf(l) => Err(*l.span()), - ::tt::TokenTree::Subtree(tt) => Err(tt.delimiter.open.cover(tt.delimiter.close)), + TtElement::Leaf(l) => Err(*l.span()), + TtElement::Subtree(tt, _) => Err(tt.delimiter.open.cover(tt.delimiter.close)), }) .map_err(|span| ExpandError::other(span, "expected string literal")) } @@ -732,13 +722,16 @@ fn parse_string(tt: &tt::Subtree) -> Result<(Symbol, Span), ExpandError> { fn include_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { let file_id = match include_input_to_file_id(db, arg_id, tt) { Ok(it) => it, Err(e) => { - return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) + return ExpandResult::new( + tt::TopSubtree::empty(DelimSpan { open: span, close: span }), + e, + ) } }; let span_map = db.real_span_map(file_id); @@ -754,7 +747,7 @@ fn include_expand( pub fn include_input_to_file_id( db: &dyn ExpandDatabase, arg_id: MacroCallId, - arg: &tt::Subtree, + arg: &tt::TopSubtree, ) -> Result { let (s, span) = parse_string(arg)?; relative_file(db, arg_id, s.as_str(), false, span) @@ -763,32 +756,35 @@ pub fn include_input_to_file_id( fn include_bytes_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { // FIXME: actually read the file here if the user asked for macro expansion - let res = tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(span), - token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + let res = tt::TopSubtree::invisible_from_leaves( + span, + [tt::Leaf::Literal(tt::Literal { symbol: Symbol::empty(), span, kind: tt::LitKind::ByteStrRaw(1), suffix: None, - }))]), - }; + })], + ); ExpandResult::ok(res) } fn include_str_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { let (path, span) = match parse_string(tt) { Ok(it) => it, Err(e) => { - return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) + return ExpandResult::new( + tt::TopSubtree::empty(DelimSpan { open: span, close: span }), + e, + ) } }; @@ -817,13 +813,16 @@ fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &Symbol) -> fn env_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { let (key, span) = match parse_string(tt) { Ok(it) => it, Err(e) => { - return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e) + return ExpandResult::new( + tt::TopSubtree::empty(DelimSpan { open: span, close: span }), + e, + ) } }; @@ -852,14 +851,14 @@ fn env_expand( fn option_env_expand( db: &dyn ExpandDatabase, arg_id: MacroCallId, - tt: &tt::Subtree, + tt: &tt::TopSubtree, call_site: Span, -) -> ExpandResult { +) -> ExpandResult { let (key, span) = match parse_string(tt) { Ok(it) => it, Err(e) => { return ExpandResult::new( - tt::Subtree::empty(DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(DelimSpan { open: call_site, close: call_site }), e, ) } @@ -879,11 +878,11 @@ fn option_env_expand( fn quote_expand( _db: &dyn ExpandDatabase, _arg_id: MacroCallId, - _tt: &tt::Subtree, + _tt: &tt::TopSubtree, span: Span, -) -> ExpandResult { +) -> ExpandResult { ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), + tt::TopSubtree::empty(tt::DelimSpan { open: span, close: span }), ExpandError::other(span, "quote! is not implemented"), ) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs index 418d8d9660b5..6c1abc262031 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs @@ -6,7 +6,7 @@ use span::Span; use syntax::ToSmolStr; use tt::IdentIsRaw; -use crate::name::Name; +use crate::{name::Name, tt::TopSubtreeBuilder}; pub(crate) fn dollar_crate(span: Span) -> tt::Ident { tt::Ident { sym: sym::dollar_crate.clone(), span, is_raw: tt::IdentIsRaw::No } @@ -20,119 +20,93 @@ pub(crate) fn dollar_crate(span: Span) -> tt::Ident { #[doc(hidden)] #[macro_export] macro_rules! quote_impl__ { - ($span:ident) => { - Vec::<$crate::tt::TokenTree>::new() - }; + ($span:ident $builder:ident) => {}; - ( @SUBTREE($span:ident) $delim:ident $($tt:tt)* ) => { + ( @SUBTREE($span:ident $builder:ident) $delim:ident $($tt:tt)* ) => { { - let children = $crate::builtin::quote::__quote!($span $($tt)*); - $crate::tt::Subtree { - delimiter: $crate::tt::Delimiter { - kind: $crate::tt::DelimiterKind::$delim, - open: $span, - close: $span, - }, - token_trees: $crate::builtin::quote::IntoTt::to_tokens(children).into_boxed_slice(), - } + $builder.open($crate::tt::DelimiterKind::$delim, $span); + $crate::builtin::quote::__quote!($span $builder $($tt)*); + $builder.close($span); } }; - ( @PUNCT($span:ident) $first:literal ) => { - { - vec![ - $crate::tt::Leaf::Punct($crate::tt::Punct { - char: $first, - spacing: $crate::tt::Spacing::Alone, - span: $span, - }).into() - ] - } + ( @PUNCT($span:ident $builder:ident) $first:literal ) => { + $builder.push( + $crate::tt::Leaf::Punct($crate::tt::Punct { + char: $first, + spacing: $crate::tt::Spacing::Alone, + span: $span, + }) + ); }; - ( @PUNCT($span:ident) $first:literal, $sec:literal ) => { - { - vec![ - $crate::tt::Leaf::Punct($crate::tt::Punct { - char: $first, - spacing: $crate::tt::Spacing::Joint, - span: $span, - }).into(), - $crate::tt::Leaf::Punct($crate::tt::Punct { - char: $sec, - spacing: $crate::tt::Spacing::Alone, - span: $span, - }).into() - ] - } + ( @PUNCT($span:ident $builder:ident) $first:literal, $sec:literal ) => { + $builder.extend([ + $crate::tt::Leaf::Punct($crate::tt::Punct { + char: $first, + spacing: $crate::tt::Spacing::Joint, + span: $span, + }), + $crate::tt::Leaf::Punct($crate::tt::Punct { + char: $sec, + spacing: $crate::tt::Spacing::Alone, + span: $span, + }) + ]); }; // hash variable - ($span:ident # $first:ident $($tail:tt)* ) => { - { - let token = $crate::builtin::quote::ToTokenTree::to_token($first, $span); - let mut tokens = vec![token.into()]; - let mut tail_tokens = $crate::builtin::quote::IntoTt::to_tokens($crate::builtin::quote::__quote!($span $($tail)*)); - tokens.append(&mut tail_tokens); - tokens - } + ($span:ident $builder:ident # $first:ident $($tail:tt)* ) => { + $crate::builtin::quote::ToTokenTree::to_tokens($first, $span, $builder); + $crate::builtin::quote::__quote!($span $builder $($tail)*); }; - ($span:ident ## $first:ident $($tail:tt)* ) => { - { - let mut tokens = $first.into_iter().map(|it| $crate::builtin::quote::ToTokenTree::to_token(it, $span)).collect::>(); - let mut tail_tokens = $crate::builtin::quote::IntoTt::to_tokens($crate::builtin::quote::__quote!($span $($tail)*)); - tokens.append(&mut tail_tokens); - tokens - } - }; + ($span:ident $builder:ident ## $first:ident $($tail:tt)* ) => {{ + ::std::iter::IntoIterator::into_iter($first).for_each(|it| $crate::builtin::quote::ToTokenTree::to_tokens(it, $span, $builder)); + $crate::builtin::quote::__quote!($span $builder $($tail)*); + }}; // Brace - ($span:ident { $($tt:tt)* } ) => { $crate::builtin::quote::__quote!(@SUBTREE($span) Brace $($tt)*) }; + ($span:ident $builder:ident { $($tt:tt)* } ) => { $crate::builtin::quote::__quote!(@SUBTREE($span $builder) Brace $($tt)*) }; // Bracket - ($span:ident [ $($tt:tt)* ] ) => { $crate::builtin::quote::__quote!(@SUBTREE($span) Bracket $($tt)*) }; + ($span:ident $builder:ident [ $($tt:tt)* ] ) => { $crate::builtin::quote::__quote!(@SUBTREE($span $builder) Bracket $($tt)*) }; // Parenthesis - ($span:ident ( $($tt:tt)* ) ) => { $crate::builtin::quote::__quote!(@SUBTREE($span) Parenthesis $($tt)*) }; + ($span:ident $builder:ident ( $($tt:tt)* ) ) => { $crate::builtin::quote::__quote!(@SUBTREE($span $builder) Parenthesis $($tt)*) }; // Literal - ($span:ident $tt:literal ) => { vec![$crate::builtin::quote::ToTokenTree::to_token($tt, $span).into()] }; + ($span:ident $builder:ident $tt:literal ) => { $crate::builtin::quote::ToTokenTree::to_tokens($tt, $span, $builder) }; // Ident - ($span:ident $tt:ident ) => { - vec![ { + ($span:ident $builder:ident $tt:ident ) => { + $builder.push( $crate::tt::Leaf::Ident($crate::tt::Ident { sym: intern::Symbol::intern(stringify!($tt)), span: $span, is_raw: tt::IdentIsRaw::No, - }).into() - }] + }) + ); }; // Puncts // FIXME: Not all puncts are handled - ($span:ident -> ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '-', '>')}; - ($span:ident => ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '=', '>')}; - ($span:ident & ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '&')}; - ($span:ident , ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ',')}; - ($span:ident : ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ':')}; - ($span:ident ; ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ';')}; - ($span:ident :: ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ':', ':')}; - ($span:ident . ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '.')}; - ($span:ident < ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '<')}; - ($span:ident > ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '>')}; - ($span:ident ! ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '!')}; - ($span:ident # ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '#')}; - ($span:ident $ ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '$')}; - ($span:ident * ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '*')}; + ($span:ident $builder:ident -> ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '-', '>')}; + ($span:ident $builder:ident => ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '=', '>')}; + ($span:ident $builder:ident & ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '&')}; + ($span:ident $builder:ident , ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) ',')}; + ($span:ident $builder:ident : ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) ':')}; + ($span:ident $builder:ident ; ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) ';')}; + ($span:ident $builder:ident :: ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) ':', ':')}; + ($span:ident $builder:ident . ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '.')}; + ($span:ident $builder:ident < ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '<')}; + ($span:ident $builder:ident > ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '>')}; + ($span:ident $builder:ident ! ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '!')}; + ($span:ident $builder:ident # ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '#')}; + ($span:ident $builder:ident $ ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '$')}; + ($span:ident $builder:ident * ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '*')}; - ($span:ident $first:tt $($tail:tt)+ ) => { - { - let mut tokens = $crate::builtin::quote::IntoTt::to_tokens($crate::builtin::quote::__quote!($span $first )); - let mut tail_tokens = $crate::builtin::quote::IntoTt::to_tokens($crate::builtin::quote::__quote!($span $($tail)*)); - - tokens.append(&mut tail_tokens); - tokens - } - }; + ($span:ident $builder:ident $first:tt $($tail:tt)+ ) => {{ + $crate::builtin::quote::__quote!($span $builder $first); + $crate::builtin::quote::__quote!($span $builder $($tail)*); + }}; } pub use quote_impl__ as __quote; @@ -141,52 +115,68 @@ pub use quote_impl__ as __quote; #[macro_export] macro_rules! quote { ($span:ident=> $($tt:tt)* ) => { - $crate::builtin::quote::IntoTt::to_subtree($crate::builtin::quote::__quote!($span $($tt)*), $span) + { + let mut builder = $crate::tt::TopSubtreeBuilder::new($crate::tt::Delimiter { + kind: $crate::tt::DelimiterKind::Invisible, + open: $span, + close: $span, + }); + #[allow(unused)] + let builder_ref = &mut builder; + $crate::builtin::quote::__quote!($span builder_ref $($tt)*); + builder.build_skip_top_subtree() + } } } pub(super) use quote; -pub trait IntoTt { - fn to_subtree(self, span: Span) -> crate::tt::Subtree; - fn to_tokens(self) -> Vec; -} - -impl IntoTt for Vec { - fn to_subtree(self, span: Span) -> crate::tt::Subtree { - crate::tt::Subtree { - delimiter: crate::tt::Delimiter::invisible_spanned(span), - token_trees: self.into_boxed_slice(), - } - } - - fn to_tokens(self) -> Vec { - self - } -} - -impl IntoTt for crate::tt::Subtree { - fn to_subtree(self, _: Span) -> crate::tt::Subtree { - self - } - - fn to_tokens(self) -> Vec { - vec![crate::tt::TokenTree::Subtree(self)] - } -} - pub trait ToTokenTree { - fn to_token(self, span: Span) -> crate::tt::TokenTree; + fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder); } -impl ToTokenTree for crate::tt::TokenTree { - fn to_token(self, _: Span) -> crate::tt::TokenTree { - self +/// Wraps `TokenTreesView` with a delimiter (a subtree, but without allocating). +pub struct WithDelimiter<'a> { + pub delimiter: crate::tt::Delimiter, + pub token_trees: crate::tt::TokenTreesView<'a>, +} + +impl ToTokenTree for WithDelimiter<'_> { + fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder) { + builder.open(self.delimiter.kind, self.delimiter.open); + self.token_trees.to_tokens(span, builder); + builder.close(self.delimiter.close); } } -impl ToTokenTree for crate::tt::Subtree { - fn to_token(self, _: Span) -> crate::tt::TokenTree { - self.into() +impl ToTokenTree for crate::tt::TokenTreesView<'_> { + fn to_tokens(self, _: Span, builder: &mut TopSubtreeBuilder) { + builder.extend_with_tt(self); + } +} + +impl ToTokenTree for crate::tt::SubtreeView<'_> { + fn to_tokens(self, _: Span, builder: &mut TopSubtreeBuilder) { + builder.extend_with_tt(self.as_token_trees()); + } +} + +impl ToTokenTree for crate::tt::TopSubtree { + fn to_tokens(self, _: Span, builder: &mut TopSubtreeBuilder) { + builder.extend_tt_dangerous(self.0); + } +} + +impl ToTokenTree for crate::tt::TtElement<'_> { + fn to_tokens(self, _: Span, builder: &mut TopSubtreeBuilder) { + match self { + crate::tt::TtElement::Leaf(leaf) => builder.push(leaf.clone()), + crate::tt::TtElement::Subtree(subtree, subtree_iter) => { + builder.extend_tt_dangerous( + std::iter::once(crate::tt::TokenTree::Subtree(subtree.clone())) + .chain(subtree_iter.remaining().flat_tokens().iter().cloned()), + ); + } + } } } @@ -194,18 +184,17 @@ macro_rules! impl_to_to_tokentrees { ($($span:ident: $ty:ty => $this:ident $im:block;)*) => { $( impl ToTokenTree for $ty { - fn to_token($this, $span: Span) -> crate::tt::TokenTree { + fn to_tokens($this, $span: Span, builder: &mut TopSubtreeBuilder) { let leaf: crate::tt::Leaf = $im.into(); - leaf.into() + builder.push(leaf); } } )* } } - impl ToTokenTree for &T { - fn to_token(self, span: Span) -> crate::tt::TokenTree { - self.clone().to_token(span) + fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder) { + self.clone().to_tokens(span, builder); } } @@ -316,18 +305,15 @@ mod tests { // } let struct_name = mk_ident("Foo"); let fields = [mk_ident("name"), mk_ident("id")]; - let fields = fields - .iter() - .flat_map(|it| quote!(DUMMY =>#it: self.#it.clone(), ).token_trees.into_vec()); + let fields = fields.iter().map(|it| quote!(DUMMY =>#it: self.#it.clone(), )); - let list = crate::tt::Subtree { - delimiter: crate::tt::Delimiter { - kind: crate::tt::DelimiterKind::Brace, - open: DUMMY, - close: DUMMY, - }, - token_trees: fields.collect(), - }; + let mut builder = tt::TopSubtreeBuilder::new(crate::tt::Delimiter { + kind: crate::tt::DelimiterKind::Brace, + open: DUMMY, + close: DUMMY, + }); + fields.for_each(|field| builder.extend_with_tt(field.view().as_token_trees())); + let list = builder.build(); let quoted = quote! {DUMMY => impl Clone for #struct_name { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index fa400378f3af..f4e80ef9e260 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -28,7 +28,7 @@ use crate::{ MacroDefId, MacroDefKind, MacroFileId, }; /// This is just to ensure the types of smart_macro_arg and macro_arg are the same -type MacroArgResult = (Arc, SyntaxFixupUndoInfo, Span); +type MacroArgResult = (Arc, SyntaxFixupUndoInfo, Span); /// Total limit on the number of tokens produced by any macro invocation. /// /// If an invocation produces more tokens than this limit, it will not be stored in the database and @@ -123,7 +123,7 @@ pub trait ExpandDatabase: SourceDatabase { /// proc macros, since they are not deterministic in general, and /// non-determinism breaks salsa in a very, very, very bad way. /// @edwin0cheng heroically debugged this once! See #4315 for details - fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult>; + fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult>; /// Retrieves the span to be used for a proc-macro expansions spans. /// This is a firewall query as it requires parsing the file, which we don't want proc-macros to /// directly depend on as that would cause to frequent invalidations, mainly because of the @@ -251,7 +251,7 @@ pub fn expand_speculative( span, DocCommentDesugarMode::ProcMacro, ); - tree.delimiter = tt::Delimiter::invisible_spanned(span); + *tree.top_subtree_delimiter_mut() = tt::Delimiter::invisible_spanned(span); Some(tree) } @@ -266,7 +266,7 @@ pub fn expand_speculative( let mut speculative_expansion = match loc.def.kind { MacroDefKind::ProcMacro(ast, expander, _) => { let span = db.proc_macro_span(ast); - tt.delimiter = tt::Delimiter::invisible_spanned(span); + *tt.top_subtree_delimiter_mut() = tt::Delimiter::invisible_spanned(span); expander.expand( db, loc.def.krate, @@ -429,10 +429,10 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let dummy_tt = |kind| { ( - Arc::new(tt::Subtree { - delimiter: tt::Delimiter { open: span, close: span, kind }, - token_trees: Box::default(), - }), + Arc::new(tt::TopSubtree::from_token_trees( + tt::Delimiter { open: span, close: span, kind }, + tt::TokenTreesView::new(&[]), + )), SyntaxFixupUndoInfo::default(), span, ) @@ -479,7 +479,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { ); if loc.def.is_proc_macro() { // proc macros expect their inputs without parentheses, MBEs expect it with them included - tt.delimiter.kind = tt::DelimiterKind::Invisible; + tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible; } return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span); } @@ -537,7 +537,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { if loc.def.is_proc_macro() { // proc macros expect their inputs without parentheses, MBEs expect it with them included - tt.delimiter.kind = tt::DelimiterKind::Invisible; + tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible; } (Arc::new(tt), undo_info, span) @@ -592,7 +592,7 @@ fn macro_expand( db: &dyn ExpandDatabase, macro_call_id: MacroCallId, loc: MacroCallLoc, -) -> ExpandResult<(CowArc, MatchedArmIndex)> { +) -> ExpandResult<(CowArc, MatchedArmIndex)> { let _p = tracing::info_span!("macro_expand").entered(); let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind { @@ -655,12 +655,7 @@ fn macro_expand( // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { return value - .map(|()| { - CowArc::Owned(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(span), - token_trees: Box::new([]), - }) - }) + .map(|()| CowArc::Owned(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)))) .zip_val(matched_arm); } } @@ -679,7 +674,10 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId) -> Span { span_map.span_for_range(range) } -fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult> { +fn expand_proc_macro( + db: &dyn ExpandDatabase, + id: MacroCallId, +) -> ExpandResult> { let loc = db.lookup_intern_macro_call(id); let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(id, &loc.kind); @@ -709,12 +707,7 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult ExpandResult (Parse, ExpansionSpanMap) { @@ -737,7 +730,8 @@ fn token_tree_to_syntax_node( syntax_bridge::token_tree_to_syntax_node(tt, entry_point, edition) } -fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> { +fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> { + let tt = tt.top_subtree(); let count = tt.count(); if TOKEN_LIMIT.check(count).is_err() { Err(ExpandResult { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index 86dd4aef090f..d1c39f32ca38 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -26,14 +26,14 @@ impl DeclarativeMacroExpander { pub fn expand( &self, db: &dyn ExpandDatabase, - tt: tt::Subtree, + tt: tt::TopSubtree, call_id: MacroCallId, span: Span, - ) -> ExpandResult<(tt::Subtree, Option)> { + ) -> ExpandResult<(tt::TopSubtree, Option)> { let loc = db.lookup_intern_macro_call(call_id); match self.mac.err() { Some(_) => ExpandResult::new( - (tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), None), + (tt::TopSubtree::empty(tt::DelimSpan { open: span, close: span }), None), ExpandError::new(span, ExpandErrorKind::MacroDefinition), ), None => self @@ -50,13 +50,13 @@ impl DeclarativeMacroExpander { pub fn expand_unhygienic( &self, - tt: tt::Subtree, + tt: tt::TopSubtree, call_site: Span, def_site_edition: Edition, - ) -> ExpandResult { + ) -> ExpandResult { match self.mac.err() { Some(_) => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }), ExpandError::new(call_site, ExpandErrorKind::MacroDefinition), ), None => self @@ -78,7 +78,7 @@ impl DeclarativeMacroExpander { let transparency = |node| { // ... would be nice to have the item tree here let attrs = RawAttrs::new(db, node, map.as_ref()).filter(db, def_crate); - match &*attrs + match attrs .iter() .find(|it| { it.path @@ -87,7 +87,8 @@ impl DeclarativeMacroExpander { .unwrap_or(false) })? .token_tree_value()? - .token_trees + .token_trees() + .flat_tokens() { [tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &i.sym { s if *s == sym::transparent => Some(Transparency::Transparent), diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index 1dadfe2ba99b..f476d1b564c4 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -89,7 +89,7 @@ pub fn expand_eager_macro_input( DocCommentDesugarMode::Mbe, ); - subtree.delimiter.kind = crate::tt::DelimiterKind::Invisible; + subtree.top_subtree_delimiter_mut().kind = crate::tt::DelimiterKind::Invisible; let loc = MacroCallLoc { def, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index 0af29681a13f..3d2d52a0afe3 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -3,7 +3,6 @@ use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; -use smallvec::SmallVec; use span::{ ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, FIXUP_ERASED_FILE_AST_ID_MARKER, ROOT_ERASED_FILE_AST_ID, @@ -19,7 +18,7 @@ use tt::Spacing; use crate::{ span_map::SpanMapRef, - tt::{Ident, Leaf, Punct, Subtree}, + tt::{self, Ident, Leaf, Punct, TopSubtree}, }; /// The result of calculating fixes for a syntax node -- a bunch of changes @@ -36,7 +35,7 @@ pub(crate) struct SyntaxFixups { #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SyntaxFixupUndoInfo { // FIXME: ThinArc<[Subtree]> - original: Option>>, + original: Option>>, } impl SyntaxFixupUndoInfo { @@ -110,7 +109,7 @@ pub(crate) fn fixup_syntax( } }, ast::ExprStmt(it) => { - let needs_semi = it.semicolon_token().is_none() && it.expr().map_or(false, |e| e.syntax().kind() != SyntaxKind::BLOCK_EXPR); + let needs_semi = it.semicolon_token().is_none() && it.expr().is_some_and(|e| e.syntax().kind() != SyntaxKind::BLOCK_EXPR); if needs_semi { append.insert(node.clone().into(), vec![ Leaf::Punct(Punct { @@ -369,68 +368,126 @@ fn has_error_to_handle(node: &SyntaxNode) -> bool { has_error(node) || node.children().any(|c| !can_handle_error(&c) && has_error_to_handle(&c)) } -pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo) { +pub(crate) fn reverse_fixups(tt: &mut TopSubtree, undo_info: &SyntaxFixupUndoInfo) { let Some(undo_info) = undo_info.original.as_deref() else { return }; let undo_info = &**undo_info; + let delimiter = tt.top_subtree_delimiter_mut(); #[allow(deprecated)] if never!( - tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID - || tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID + delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID + || delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID ) { let span = |file_id| Span { range: TextRange::empty(TextSize::new(0)), anchor: SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID }, ctx: SyntaxContextId::ROOT, }; - tt.delimiter.open = span(tt.delimiter.open.anchor.file_id); - tt.delimiter.close = span(tt.delimiter.close.anchor.file_id); + delimiter.open = span(delimiter.open.anchor.file_id); + delimiter.close = span(delimiter.close.anchor.file_id); } reverse_fixups_(tt, undo_info); } -fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { - let tts = std::mem::take(&mut tt.token_trees).into_vec(); - tt.token_trees = tts - .into_iter() - // delete all fake nodes - .filter(|tt| match tt { - tt::TokenTree::Leaf(leaf) => { - let span = leaf.span(); - let is_real_leaf = span.anchor.ast_id != FIXUP_DUMMY_AST_ID; - let is_replaced_node = span.range.end() == FIXUP_DUMMY_RANGE_END; - is_real_leaf || is_replaced_node +#[derive(Debug)] +enum TransformTtAction<'a> { + Keep, + ReplaceWith(tt::TokenTreesView<'a>), +} + +impl TransformTtAction<'_> { + fn remove() -> Self { + Self::ReplaceWith(tt::TokenTreesView::new(&[])) + } +} + +/// This function takes a token tree, and calls `callback` with each token tree in it. +/// Then it does what the callback says: keeps the tt or replaces it with a (possibly empty) +/// tts view. +fn transform_tt<'a, 'b>( + tt: &'a mut Vec, + mut callback: impl FnMut(&mut tt::TokenTree) -> TransformTtAction<'b>, +) { + // We need to keep a stack of the currently open subtrees, because we need to update + // them if we change the number of items in them. + let mut subtrees_stack = Vec::new(); + let mut i = 0; + while i < tt.len() { + 'pop_finished_subtrees: while let Some(&subtree_idx) = subtrees_stack.last() { + let tt::TokenTree::Subtree(subtree) = &tt[subtree_idx] else { + unreachable!("non-subtree on subtrees stack"); + }; + if i >= subtree_idx + 1 + subtree.usize_len() { + subtrees_stack.pop(); + } else { + break 'pop_finished_subtrees; } - tt::TokenTree::Subtree(_) => true, - }) - .flat_map(|tt| match tt { - tt::TokenTree::Subtree(mut tt) => { - if tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID - || tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID - { - // Even though fixup never creates subtrees with fixup spans, the old proc-macro server - // might copy them if the proc-macro asks for it, so we need to filter those out - // here as well. - return SmallVec::new_const(); + } + + let action = callback(&mut tt[i]); + match action { + TransformTtAction::Keep => { + // This cannot be shared with the replaced case, because then we may push the same subtree + // twice, and will update it twice which will lead to errors. + if let tt::TokenTree::Subtree(_) = &tt[i] { + subtrees_stack.push(i); } - reverse_fixups_(&mut tt, undo_info); - SmallVec::from_const([tt.into()]) + + i += 1; } - tt::TokenTree::Leaf(leaf) => { - if leaf.span().anchor.ast_id == FIXUP_DUMMY_AST_ID { - // we have a fake node here, we need to replace it again with the original - let original = undo_info[u32::from(leaf.span().range.start()) as usize].clone(); - if original.delimiter.kind == tt::DelimiterKind::Invisible { - SmallVec::from(original.token_trees.into_vec()) - } else { - SmallVec::from_const([original.into()]) - } - } else { - // just a normal leaf - SmallVec::from_const([leaf.into()]) + TransformTtAction::ReplaceWith(replacement) => { + let old_len = 1 + match &tt[i] { + tt::TokenTree::Leaf(_) => 0, + tt::TokenTree::Subtree(subtree) => subtree.usize_len(), + }; + let len_diff = replacement.len() as i64 - old_len as i64; + tt.splice(i..i + old_len, replacement.flat_tokens().iter().cloned()); + // `+1` for the loop. + i = i.checked_add_signed(len_diff as isize + 1).unwrap(); + + for &subtree_idx in &subtrees_stack { + let tt::TokenTree::Subtree(subtree) = &mut tt[subtree_idx] else { + unreachable!("non-subtree on subtrees stack"); + }; + subtree.len = (i64::from(subtree.len) + len_diff).try_into().unwrap(); } } - }) - .collect(); + } + } +} + +fn reverse_fixups_(tt: &mut TopSubtree, undo_info: &[TopSubtree]) { + let mut tts = std::mem::take(&mut tt.0).into_vec(); + transform_tt(&mut tts, |tt| match tt { + tt::TokenTree::Leaf(leaf) => { + let span = leaf.span(); + let is_real_leaf = span.anchor.ast_id != FIXUP_DUMMY_AST_ID; + let is_replaced_node = span.range.end() == FIXUP_DUMMY_RANGE_END; + if !is_real_leaf && !is_replaced_node { + return TransformTtAction::remove(); + } + + if !is_real_leaf { + // we have a fake node here, we need to replace it again with the original + let original = &undo_info[u32::from(leaf.span().range.start()) as usize]; + TransformTtAction::ReplaceWith(original.view().strip_invisible()) + } else { + // just a normal leaf + TransformTtAction::Keep + } + } + tt::TokenTree::Subtree(tt) => { + if tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID + || tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID + { + // Even though fixup never creates subtrees with fixup spans, the old proc-macro server + // might copy them if the proc-macro asks for it, so we need to filter those out + // here as well. + return TransformTtAction::remove(); + } + TransformTtAction::Keep + } + }); + tt.0 = tts.into_boxed_slice(); } #[cfg(test)] @@ -458,16 +515,18 @@ mod tests { } } - fn check_subtree_eq(a: &tt::Subtree, b: &tt::Subtree) -> bool { - a.delimiter.kind == b.delimiter.kind - && a.token_trees.len() == b.token_trees.len() - && a.token_trees.iter().zip(b.token_trees.iter()).all(|(a, b)| check_tt_eq(a, b)) + fn check_subtree_eq(a: &tt::TopSubtree, b: &tt::TopSubtree) -> bool { + let a = a.view().as_token_trees().flat_tokens(); + let b = b.view().as_token_trees().flat_tokens(); + a.len() == b.len() && std::iter::zip(a, b).all(|(a, b)| check_tt_eq(a, b)) } fn check_tt_eq(a: &tt::TokenTree, b: &tt::TokenTree) -> bool { match (a, b) { (tt::TokenTree::Leaf(a), tt::TokenTree::Leaf(b)) => check_leaf_eq(a, b), - (tt::TokenTree::Subtree(a), tt::TokenTree::Subtree(b)) => check_subtree_eq(a, b), + (tt::TokenTree::Subtree(a), tt::TokenTree::Subtree(b)) => { + a.delimiter.kind == b.delimiter.kind + } _ => false, } } 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 5aafe6db5271..a0c4c125db47 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -70,12 +70,17 @@ pub mod tt { pub type Delimiter = ::tt::Delimiter; pub type DelimSpan = ::tt::DelimSpan; pub type Subtree = ::tt::Subtree; - pub type SubtreeBuilder = ::tt::SubtreeBuilder; pub type Leaf = ::tt::Leaf; pub type Literal = ::tt::Literal; pub type Punct = ::tt::Punct; pub type Ident = ::tt::Ident; pub type TokenTree = ::tt::TokenTree; + pub type TopSubtree = ::tt::TopSubtree; + pub type TopSubtreeBuilder = ::tt::TopSubtreeBuilder; + pub type TokenTreesView<'a> = ::tt::TokenTreesView<'a, Span>; + pub type SubtreeView<'a> = ::tt::SubtreeView<'a, Span>; + pub type TtElement<'a> = ::tt::iter::TtElement<'a, Span>; + pub type TtIter<'a> = ::tt::iter::TtIter<'a, Span>; } #[macro_export] @@ -284,7 +289,7 @@ impl MacroDefKind { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct EagerCallInfo { /// The expanded argument of the eager macro. - arg: Arc, + arg: Arc, /// Call id of the eager macro's input file (this is the macro file for its fully expanded input). arg_id: MacroCallId, error: Option, @@ -320,7 +325,7 @@ pub enum MacroCallKind { ast_id: AstId, // FIXME: This shouldn't be here, we can derive this from `invoc_attr_index` // but we need to fix the `cfg_attr` handling first. - attr_args: Option>, + attr_args: Option>, /// Syntactical index of the invoking `#[attribute]`. /// /// Outer attributes are counted first, then inner attributes. This does not support @@ -1044,7 +1049,7 @@ impl ExpandTo { if parent.kind() == MACRO_EXPR && parent .parent() - .map_or(false, |p| matches!(p.kind(), EXPR_STMT | STMT_LIST | MACRO_STMTS)) + .is_some_and(|p| matches!(p.kind(), EXPR_STMT | STMT_LIST | MACRO_STMTS)) { return ExpandTo::Statements; } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index dcf2af399797..7ecf5219873b 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -58,7 +58,7 @@ impl ModPath { convert_path(db, path, span_for_range) } - pub fn from_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option { + pub fn from_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Option { convert_path_tt(db, tt) } @@ -315,10 +315,10 @@ fn convert_path( Some(mod_path) } -fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option { +fn convert_path_tt(db: &dyn ExpandDatabase, tt: tt::TokenTreesView<'_>) -> Option { let mut leaves = tt.iter().filter_map(|tt| match tt { - tt::TokenTree::Leaf(leaf) => Some(leaf), - tt::TokenTree::Subtree(_) => None, + tt::TtElement::Leaf(leaf) => Some(leaf), + tt::TtElement::Subtree(..) => None, }); let mut segments = smallvec::smallvec![]; let kind = match leaves.next()? { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs index fe09f0307c9e..07808fea85b3 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs @@ -23,14 +23,14 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe { /// [`ProcMacroKind::Attr`]), environment variables, and span information. fn expand( &self, - subtree: &tt::Subtree, - attrs: Option<&tt::Subtree>, + subtree: &tt::TopSubtree, + attrs: Option<&tt::TopSubtree>, env: &Env, def_site: Span, call_site: Span, mixed_site: Span, current_dir: Option, - ) -> Result; + ) -> Result; } #[derive(Debug)] @@ -201,23 +201,23 @@ impl CustomProcMacroExpander { db: &dyn ExpandDatabase, def_crate: CrateId, calling_crate: CrateId, - tt: &tt::Subtree, - attr_arg: Option<&tt::Subtree>, + tt: &tt::TopSubtree, + attr_arg: Option<&tt::TopSubtree>, def_site: Span, call_site: Span, mixed_site: Span, - ) -> ExpandResult { + ) -> ExpandResult { match self.proc_macro_id { Self::PROC_MACRO_ATTR_DISABLED => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }), ExpandError::new(call_site, ExpandErrorKind::ProcMacroAttrExpansionDisabled), ), Self::MISSING_EXPANDER => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }), ExpandError::new(call_site, ExpandErrorKind::MissingProcMacroExpander(def_crate)), ), Self::DISABLED_ID => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(tt::DelimSpan { open: call_site, close: call_site }), ExpandError::new(call_site, ExpandErrorKind::MacroDisabled), ), id => { @@ -226,7 +226,10 @@ impl CustomProcMacroExpander { Ok(proc_macro) => proc_macro, Err(e) => { return ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(tt::DelimSpan { + open: call_site, + close: call_site, + }), e, ) } @@ -260,7 +263,10 @@ impl CustomProcMacroExpander { } ProcMacroExpansionError::System(text) | ProcMacroExpansionError::Panic(text) => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), + tt::TopSubtree::empty(tt::DelimSpan { + open: call_site, + close: call_site, + }), ExpandError::new( call_site, ExpandErrorKind::ProcMacroPanic(text.into_boxed_str()), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index 55d0edd5e0c9..9f01f1eb2590 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -13,7 +13,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; use base_db::CrateId; use hir_def::{ - data::adt::StructFlags, + data::{adt::StructFlags, TraitFlags}, hir::Movability, lang_item::{LangItem, LangItemTarget}, AssocItemId, BlockId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup, @@ -675,13 +675,13 @@ pub(crate) fn trait_datum_query( let generic_params = generics(db.upcast(), trait_.into()); let bound_vars = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST); let flags = rust_ir::TraitFlags { - auto: trait_data.is_auto, + auto: trait_data.flags.contains(TraitFlags::IS_AUTO), upstream: trait_.lookup(db.upcast()).container.krate() != krate, non_enumerable: true, coinductive: false, // only relevant for Chalk testing // FIXME: set these flags correctly marker: false, - fundamental: trait_data.fundamental, + fundamental: trait_data.flags.contains(TraitFlags::IS_FUNDAMENTAL), }; let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect(); @@ -950,11 +950,18 @@ pub(crate) fn fn_def_datum_query(db: &dyn HirDatabase, fn_def_id: FnDefId) -> Ar pub(crate) fn fn_def_variance_query(db: &dyn HirDatabase, fn_def_id: FnDefId) -> Variances { let callable_def: CallableDefId = from_chalk(db, fn_def_id); - let generic_params = - generics(db.upcast(), GenericDefId::from_callable(db.upcast(), callable_def)); Variances::from_iter( Interner, - std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()), + db.variances_of(GenericDefId::from_callable(db.upcast(), callable_def)) + .as_deref() + .unwrap_or_default() + .iter() + .map(|v| match v { + crate::variance::Variance::Covariant => chalk_ir::Variance::Covariant, + crate::variance::Variance::Invariant => chalk_ir::Variance::Invariant, + crate::variance::Variance::Contravariant => chalk_ir::Variance::Contravariant, + crate::variance::Variance::Bivariant => chalk_ir::Variance::Invariant, + }), ) } @@ -962,10 +969,14 @@ pub(crate) fn adt_variance_query( db: &dyn HirDatabase, chalk_ir::AdtId(adt_id): AdtId, ) -> Variances { - let generic_params = generics(db.upcast(), adt_id.into()); Variances::from_iter( Interner, - std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()), + db.variances_of(adt_id.into()).as_deref().unwrap_or_default().iter().map(|v| match v { + crate::variance::Variance::Covariant => chalk_ir::Variance::Covariant, + crate::variance::Variance::Invariant => chalk_ir::Variance::Invariant, + crate::variance::Variance::Contravariant => chalk_ir::Variance::Contravariant, + crate::variance::Variance::Bivariant => chalk_ir::Variance::Invariant, + }), ) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 302558162ac9..51c178b90d72 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -443,13 +443,25 @@ impl ProjectionTyExt for ProjectionTy { } pub trait DynTyExt { - fn principal(&self) -> Option<&TraitRef>; + fn principal(&self) -> Option>>; + fn principal_id(&self) -> Option>; } impl DynTyExt for DynTy { - fn principal(&self) -> Option<&TraitRef> { + fn principal(&self) -> Option>> { + self.bounds.as_ref().filter_map(|bounds| { + bounds.interned().first().and_then(|b| { + b.as_ref().filter_map(|b| match b { + crate::WhereClause::Implemented(trait_ref) => Some(trait_ref), + _ => None, + }) + }) + }) + } + + fn principal_id(&self) -> Option> { self.bounds.skip_binders().interned().first().and_then(|b| match b.skip_binders() { - crate::WhereClause::Implemented(trait_ref) => Some(trait_ref), + crate::WhereClause::Implemented(trait_ref) => Some(trait_ref.trait_id), _ => None, }) } 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 6856eaa3e02f..6b0568266708 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -271,6 +271,10 @@ pub trait HirDatabase: DefDatabase + Upcast { #[ra_salsa::invoke(chalk_db::adt_variance_query)] fn adt_variance(&self, adt_id: chalk_db::AdtId) -> chalk_db::Variances; + #[ra_salsa::invoke(crate::variance::variances_of)] + #[ra_salsa::cycle(crate::variance::variances_of_cycle)] + fn variances_of(&self, def: GenericDefId) -> Option>; + #[ra_salsa::invoke(chalk_db::associated_ty_value_query)] fn associated_ty_value( &self, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs index aa0c9e30be10..348f8a0f4a85 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs @@ -49,7 +49,7 @@ fn is_camel_case(name: &str) -> bool { let mut fst = None; // start with a non-lowercase letter rather than non-uppercase // ones (some scripts don't have a concept of upper/lowercase) - name.chars().next().map_or(true, |c| !c.is_lowercase()) + name.chars().next().is_none_or(|c| !c.is_lowercase()) && !name.contains("__") && !name.chars().any(|snd| { let ret = match fst { 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 92404e3a10e2..7f9f0c0de197 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 @@ -295,7 +295,7 @@ impl ExprValidator { path, self.body.expr_path_hygiene(scrutinee_expr), ); - value_or_partial.map_or(true, |v| !matches!(v, ValueNs::StaticId(_))) + value_or_partial.is_none_or(|v| !matches!(v, ValueNs::StaticId(_))) } Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) { TyKind::Adt(adt, ..) @@ -447,7 +447,7 @@ impl ExprValidator { loop { let parent = top_if_expr.syntax().parent(); let has_parent_expr_stmt_or_stmt_list = - parent.as_ref().map_or(false, |node| { + parent.as_ref().is_some_and(|node| { ast::ExprStmt::can_cast(node.kind()) | ast::StmtList::can_cast(node.kind()) }); @@ -529,7 +529,7 @@ impl FilterMapNextChecker { let is_dyn_trait = self .prev_receiver_ty .as_ref() - .map_or(false, |it| it.strip_references().dyn_trait().is_some()); + .is_some_and(|it| it.strip_references().dyn_trait().is_some()); if *receiver_expr_id == prev_filter_map_expr_id && !is_dyn_trait { return Some(()); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 5452f5c680df..2b854310a15e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -314,7 +314,7 @@ impl<'db> MatchCheckCtx<'db> { } } -impl<'db> PatCx for MatchCheckCtx<'db> { +impl PatCx for MatchCheckCtx<'_> { type Error = (); type Ty = Ty; type VariantIdx = EnumVariantContiguousIndex; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index de8ce56df641..a4e052a0362a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -474,7 +474,9 @@ impl HirDisplay for ProjectionTy { let trait_ref = self.trait_ref(f.db); write!(f, "<")?; - fmt_trait_ref(f, &trait_ref, true)?; + trait_ref.self_type_parameter(Interner).hir_fmt(f)?; + write!(f, " as ")?; + trait_ref.hir_fmt(f)?; write!( f, ">::{}", @@ -1775,32 +1777,14 @@ fn write_bounds_like_dyn_trait( Ok(()) } -fn fmt_trait_ref( - f: &mut HirFormatter<'_>, - tr: &TraitRef, - use_as: bool, -) -> Result<(), HirDisplayError> { - if f.should_truncate() { - return write!(f, "{TYPE_HINT_TRUNCATION}"); - } - - tr.self_type_parameter(Interner).hir_fmt(f)?; - if use_as { - write!(f, " as ")?; - } else { - write!(f, ": ")?; - } - let trait_ = tr.hir_trait_id(); - f.start_location_link(trait_.into()); - write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast(), f.edition()))?; - f.end_location_link(); - let substs = tr.substitution.as_slice(Interner); - hir_fmt_generics(f, &substs[1..], None, substs[0].ty(Interner)) -} - impl HirDisplay for TraitRef { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - fmt_trait_ref(f, self, false) + let trait_ = self.hir_trait_id(); + f.start_location_link(trait_.into()); + write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast(), f.edition()))?; + f.end_location_link(); + let substs = self.substitution.as_slice(Interner); + hir_fmt_generics(f, &substs[1..], None, substs[0].ty(Interner)) } } @@ -1811,10 +1795,17 @@ impl HirDisplay for WhereClause { } match self { - WhereClause::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, + WhereClause::Implemented(trait_ref) => { + trait_ref.self_type_parameter(Interner).hir_fmt(f)?; + write!(f, ": ")?; + trait_ref.hir_fmt(f)?; + } WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => { write!(f, "<")?; - fmt_trait_ref(f, &projection_ty.trait_ref(f.db), true)?; + let trait_ref = &projection_ty.trait_ref(f.db); + trait_ref.self_type_parameter(Interner).hir_fmt(f)?; + write!(f, " as ")?; + trait_ref.hir_fmt(f)?; write!(f, ">::",)?; let type_alias = from_assoc_type_id(projection_ty.associated_ty_id); f.start_location_link(type_alias.into()); 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 fadf8aca9982..6a01579bccc9 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 @@ -9,8 +9,8 @@ use chalk_ir::{ }; use chalk_solve::rust_ir::InlineBound; use hir_def::{ - lang_item::LangItem, AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId, - TypeAliasId, + data::TraitFlags, lang_item::LangItem, AssocItemId, ConstId, FunctionId, GenericDefId, + HasModule, TraitId, TypeAliasId, }; use rustc_hash::FxHashSet; use smallvec::SmallVec; @@ -432,7 +432,7 @@ where // Allow `impl AutoTrait` predicates if let WhereClause::Implemented(TraitRef { trait_id, substitution }) = pred { let trait_data = db.trait_data(from_chalk_trait_id(*trait_id)); - if trait_data.is_auto + if trait_data.flags.contains(TraitFlags::IS_AUTO) && substitution .as_slice(Interner) .first() 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 fe7541d23747..abbf2a4f2efd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -26,14 +26,14 @@ use triomphe::Arc; use crate::{db::HirDatabase, lt_to_placeholder_idx, to_placeholder_idx, Interner, Substitution}; -pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics { +pub fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics { let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def))); let params = db.generic_params(def); let has_trait_self_param = params.trait_self_param().is_some(); Generics { def, params, parent_generics, has_trait_self_param } } #[derive(Clone, Debug)] -pub(crate) struct Generics { +pub struct Generics { def: GenericDefId, params: Arc, parent_generics: Option>, @@ -153,7 +153,7 @@ impl Generics { (parent_len, self_param, type_params, const_params, impl_trait_params, lifetime_params) } - pub(crate) fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option { + pub fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option { self.find_type_or_const_param(param) } @@ -174,7 +174,7 @@ impl Generics { } } - pub(crate) fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option { + pub fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option { self.find_lifetime(lifetime) } 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 dbee5a1a919f..25bb3a76de2c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -16,6 +16,7 @@ pub(crate) mod cast; pub(crate) mod closure; mod coerce; +mod diagnostics; mod expr; mod mutability; mod pat; @@ -57,15 +58,20 @@ use crate::{ db::HirDatabase, fold_tys, generics::Generics, - infer::{coerce::CoerceMany, expr::ExprIsRead, unify::InferenceTable}, - lower::{ImplTraitLoweringMode, TyLoweringDiagnostic}, + infer::{ + coerce::CoerceMany, + diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext}, + expr::ExprIsRead, + unify::InferenceTable, + }, + lower::{diagnostics::TyLoweringDiagnostic, ImplTraitLoweringMode}, mir::MirSpan, to_assoc_type_id, traits::FnTrait, utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder}, AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId, - ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, ProjectionTy, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, + ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, + PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, }; // This lint has a false positive here. See the link below for details. @@ -276,6 +282,10 @@ pub enum InferenceDiagnostic { source: InferenceTyDiagnosticSource, diag: TyLoweringDiagnostic, }, + PathDiagnostic { + node: ExprOrPatId, + diag: PathLoweringDiagnostic, + }, } /// A mismatch between an expected and an inferred type. @@ -442,6 +452,7 @@ pub struct InferenceResult { /// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of /// that which allows us to resolve a [`TupleFieldId`]s type. pub tuple_field_access_types: FxHashMap, + /// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead. pub diagnostics: Vec, pub type_of_expr: ArenaMap, /// For each pattern record the type it resolves to. @@ -579,6 +590,8 @@ pub(crate) struct InferenceContext<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) owner: DefWithBodyId, pub(crate) body: &'a Body, + /// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext + /// and resolve the path via its methods. This will ensure proper error reporting. pub(crate) resolver: Resolver, generics: OnceCell>, table: unify::InferenceTable<'a>, @@ -620,6 +633,8 @@ pub(crate) struct InferenceContext<'a> { /// comment on `InferenceContext::sort_closures` closure_dependencies: FxHashMap>, deferred_closures: FxHashMap, ExprId)>>, + + diagnostics: Diagnostics, } #[derive(Clone, Debug)] @@ -701,6 +716,7 @@ impl<'a> InferenceContext<'a> { deferred_closures: FxHashMap::default(), closure_dependencies: FxHashMap::default(), inside_assignment: false, + diagnostics: Diagnostics::default(), } } @@ -724,8 +740,10 @@ impl<'a> InferenceContext<'a> { mut result, mut deferred_cast_checks, tuple_field_accesses_rev, + diagnostics, .. } = self; + let mut diagnostics = diagnostics.finish(); // Destructure every single field so whenever new fields are added to `InferenceResult` we // don't forget to handle them here. let InferenceResult { @@ -733,7 +751,6 @@ impl<'a> InferenceContext<'a> { field_resolutions: _, variant_resolutions: _, assoc_resolutions, - diagnostics, type_of_expr, type_of_pat, type_of_binding, @@ -752,6 +769,7 @@ impl<'a> InferenceContext<'a> { mutated_bindings_in_closure: _, tuple_field_access_types: _, coercion_casts, + diagnostics: _, } = &mut result; table.fallback_if_possible(); @@ -866,6 +884,9 @@ impl<'a> InferenceContext<'a> { *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); }) .collect(); + + result.diagnostics = diagnostics; + result } @@ -1238,41 +1259,28 @@ impl<'a> InferenceContext<'a> { self.result.type_of_binding.insert(id, ty); } - fn push_diagnostic(&mut self, diagnostic: InferenceDiagnostic) { - self.result.diagnostics.push(diagnostic); - } - - fn push_ty_diagnostics( - &mut self, - source: InferenceTyDiagnosticSource, - diagnostics: Vec, - ) { - self.result.diagnostics.extend( - diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }), - ); + fn push_diagnostic(&self, diagnostic: InferenceDiagnostic) { + self.diagnostics.push(diagnostic); } fn with_ty_lowering( &mut self, types_map: &TypesMap, types_source: InferenceTyDiagnosticSource, - f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R, + f: impl FnOnce(&mut TyLoweringContext<'_>) -> R, ) -> R { - let mut ctx = crate::lower::TyLoweringContext::new( + let mut ctx = TyLoweringContext::new( self.db, &self.resolver, types_map, self.owner.into(), + &self.diagnostics, + types_source, ); - let result = f(&mut ctx); - self.push_ty_diagnostics(types_source, ctx.diagnostics); - result + f(&mut ctx) } - fn with_body_ty_lowering( - &mut self, - f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R, - ) -> R { + fn with_body_ty_lowering(&mut self, f: impl FnOnce(&mut TyLoweringContext<'_>) -> R) -> R { self.with_ty_lowering(&self.body.types, InferenceTyDiagnosticSource::Body, f) } @@ -1451,51 +1459,55 @@ impl<'a> InferenceContext<'a> { } } - fn resolve_variant(&mut self, path: Option<&Path>, value_ns: bool) -> (Ty, Option) { + fn resolve_variant( + &mut self, + node: ExprOrPatId, + path: Option<&Path>, + value_ns: bool, + ) -> (Ty, Option) { let path = match path { Some(path) => path, None => return (self.err_ty(), None), }; - let mut ctx = crate::lower::TyLoweringContext::new( + let mut ctx = TyLoweringContext::new( self.db, &self.resolver, &self.body.types, self.owner.into(), + &self.diagnostics, + InferenceTyDiagnosticSource::Body, ); let (resolution, unresolved) = if value_ns { - match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, HygieneId::ROOT) { - Some(ResolveValueResult::ValueNs(value, _)) => match value { + let Some(res) = ctx.resolve_path_in_value_ns(path, node, HygieneId::ROOT) else { + return (self.err_ty(), None); + }; + match res { + ResolveValueResult::ValueNs(value, _) => match value { ValueNs::EnumVariantId(var) => { let substs = ctx.substs_from_path(path, var.into(), true); - self.push_ty_diagnostics( - InferenceTyDiagnosticSource::Body, - ctx.diagnostics, - ); + drop(ctx); let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); return (ty, Some(var.into())); } ValueNs::StructId(strukt) => { let substs = ctx.substs_from_path(path, strukt.into(), true); - self.push_ty_diagnostics( - InferenceTyDiagnosticSource::Body, - ctx.diagnostics, - ); + drop(ctx); let ty = self.db.ty(strukt.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); return (ty, Some(strukt.into())); } ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None), - _ => return (self.err_ty(), None), + _ => { + drop(ctx); + return (self.err_ty(), None); + } }, - Some(ResolveValueResult::Partial(typens, unresolved, _)) => { - (typens, Some(unresolved)) - } - None => return (self.err_ty(), None), + ResolveValueResult::Partial(typens, unresolved, _) => (typens, Some(unresolved)), } } else { - match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { - Some((it, idx, _)) => (it, idx), + match ctx.resolve_path_in_type_ns(path, node) { + Some((it, idx)) => (it, idx), None => return (self.err_ty(), None), } }; @@ -1506,21 +1518,21 @@ impl<'a> InferenceContext<'a> { return match resolution { TypeNs::AdtId(AdtId::StructId(strukt)) => { let substs = ctx.substs_from_path(path, strukt.into(), true); - self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics); + drop(ctx); let ty = self.db.ty(strukt.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) } TypeNs::AdtId(AdtId::UnionId(u)) => { let substs = ctx.substs_from_path(path, u.into(), true); - self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics); + drop(ctx); let ty = self.db.ty(u.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); forbid_unresolved_segments((ty, Some(u.into())), unresolved) } TypeNs::EnumVariantId(var) => { let substs = ctx.substs_from_path(path, var.into(), true); - self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics); + drop(ctx); let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); forbid_unresolved_segments((ty, Some(var.into())), unresolved) @@ -1531,6 +1543,7 @@ impl<'a> InferenceContext<'a> { let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs); let Some(mut remaining_idx) = unresolved else { + drop(ctx); return self.resolve_variant_on_alias(ty, None, mod_path); }; @@ -1538,6 +1551,7 @@ impl<'a> InferenceContext<'a> { // We need to try resolving unresolved segments one by one because each may resolve // to a projection, which `TyLoweringContext` cannot handle on its own. + let mut tried_resolving_once = false; while !remaining_segments.is_empty() { let resolved_segment = path.segments().get(remaining_idx - 1).unwrap(); let current_segment = remaining_segments.take(1); @@ -1558,18 +1572,27 @@ impl<'a> InferenceContext<'a> { } } + if tried_resolving_once { + // FIXME: with `inherent_associated_types` this is allowed, but our `lower_partly_resolved_path()` + // will need to be updated to err at the correct segment. + // + // We need to stop here because otherwise the segment index passed to `lower_partly_resolved_path()` + // will be incorrect, and that can mess up error reporting. + break; + } + // `lower_partly_resolved_path()` returns `None` as type namespace unless // `remaining_segments` is empty, which is never the case here. We don't know // which namespace the new `ty` is in until normalized anyway. (ty, _) = ctx.lower_partly_resolved_path( + node, resolution, resolved_segment, current_segment, + (remaining_idx - 1) as u32, false, - &mut |_, _reason| { - // FIXME: Report an error. - }, ); + tried_resolving_once = true; ty = self.table.insert_type_vars(ty); ty = self.table.normalize_associated_types_in(ty); @@ -1582,7 +1605,7 @@ impl<'a> InferenceContext<'a> { remaining_idx += 1; remaining_segments = remaining_segments.skip(1); } - self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics); + drop(ctx); let variant = ty.as_adt().and_then(|(id, _)| match id { AdtId::StructId(s) => Some(VariantId::StructId(s)), @@ -1601,7 +1624,7 @@ impl<'a> InferenceContext<'a> { }; let substs = ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None); - self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics); + drop(ctx); let ty = self.db.ty(it.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); 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 5a251683b962..2523aba53833 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 @@ -96,8 +96,8 @@ impl InferenceContext<'_> { .map(|b| b.into_value_and_skipped_binders().0); self.deduce_closure_kind_from_predicate_clauses(clauses) } - TyKind::Dyn(dyn_ty) => dyn_ty.principal().and_then(|trait_ref| { - self.fn_trait_kind_from_trait_id(from_chalk_trait_id(trait_ref.trait_id)) + TyKind::Dyn(dyn_ty) => dyn_ty.principal_id().and_then(|trait_id| { + self.fn_trait_kind_from_trait_id(from_chalk_trait_id(trait_id)) }), TyKind::InferenceVar(ty, chalk_ir::TyVariableKind::General) => { let clauses = self.clauses_for_self_ty(*ty); @@ -992,7 +992,7 @@ impl InferenceContext<'_> { }, }, } - if self.result.pat_adjustments.get(&p).map_or(false, |it| !it.is_empty()) { + if self.result.pat_adjustments.get(&p).is_some_and(|it| !it.is_empty()) { for_mut = BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }; } self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs new file mode 100644 index 000000000000..032dc37899de --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs @@ -0,0 +1,128 @@ +//! This file contains the [`Diagnostics`] type used during inference, +//! and a wrapper around [`TyLoweringContext`] ([`InferenceTyLoweringContext`]) that replaces +//! it and takes care of diagnostics in inference. + +use std::cell::RefCell; +use std::ops::{Deref, DerefMut}; + +use hir_def::body::HygieneId; +use hir_def::hir::ExprOrPatId; +use hir_def::path::{Path, PathSegment, PathSegments}; +use hir_def::resolver::{ResolveValueResult, Resolver, TypeNs}; +use hir_def::type_ref::TypesMap; +use hir_def::TypeOwnerId; + +use crate::db::HirDatabase; +use crate::{ + InferenceDiagnostic, InferenceTyDiagnosticSource, Ty, TyLoweringContext, TyLoweringDiagnostic, +}; + +// Unfortunately, this struct needs to use interior mutability (but we encapsulate it) +// because when lowering types and paths we hold a `TyLoweringContext` that holds a reference +// to our resolver and so we cannot have mutable reference, but we really want to have +// ability to dispatch diagnostics during this work otherwise the code becomes a complete mess. +#[derive(Debug, Default, Clone)] +pub(super) struct Diagnostics(RefCell>); + +impl Diagnostics { + pub(super) fn push(&self, diagnostic: InferenceDiagnostic) { + self.0.borrow_mut().push(diagnostic); + } + + fn push_ty_diagnostics( + &self, + source: InferenceTyDiagnosticSource, + diagnostics: Vec, + ) { + self.0.borrow_mut().extend( + diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }), + ); + } + + pub(super) fn finish(self) -> Vec { + self.0.into_inner() + } +} + +pub(super) struct InferenceTyLoweringContext<'a> { + ctx: TyLoweringContext<'a>, + diagnostics: &'a Diagnostics, + source: InferenceTyDiagnosticSource, +} + +impl<'a> InferenceTyLoweringContext<'a> { + pub(super) fn new( + db: &'a dyn HirDatabase, + resolver: &'a Resolver, + types_map: &'a TypesMap, + owner: TypeOwnerId, + diagnostics: &'a Diagnostics, + source: InferenceTyDiagnosticSource, + ) -> Self { + Self { ctx: TyLoweringContext::new(db, resolver, types_map, owner), diagnostics, source } + } + + pub(super) fn resolve_path_in_type_ns( + &mut self, + path: &Path, + node: ExprOrPatId, + ) -> Option<(TypeNs, Option)> { + let diagnostics = self.diagnostics; + self.ctx.resolve_path_in_type_ns(path, &mut |_, diag| { + diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag }) + }) + } + + pub(super) fn resolve_path_in_value_ns( + &mut self, + path: &Path, + node: ExprOrPatId, + hygiene_id: HygieneId, + ) -> Option { + let diagnostics = self.diagnostics; + self.ctx.resolve_path_in_value_ns(path, hygiene_id, &mut |_, diag| { + diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag }) + }) + } + + pub(super) fn lower_partly_resolved_path( + &mut self, + node: ExprOrPatId, + resolution: TypeNs, + resolved_segment: PathSegment<'_>, + remaining_segments: PathSegments<'_>, + resolved_segment_idx: u32, + infer_args: bool, + ) -> (Ty, Option) { + let diagnostics = self.diagnostics; + self.ctx.lower_partly_resolved_path( + resolution, + resolved_segment, + remaining_segments, + resolved_segment_idx, + infer_args, + &mut |_, diag| diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag }), + ) + } +} + +impl<'a> Deref for InferenceTyLoweringContext<'a> { + type Target = TyLoweringContext<'a>; + + fn deref(&self) -> &Self::Target { + &self.ctx + } +} + +impl DerefMut for InferenceTyLoweringContext<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.ctx + } +} + +impl Drop for InferenceTyLoweringContext<'_> { + fn drop(&mut self) { + self.diagnostics + .push_ty_diagnostics(self.source, std::mem::take(&mut self.ctx.diagnostics)); + } +} 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 a13541be6958..6b6c0348dcb4 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 @@ -206,7 +206,7 @@ impl InferenceContext<'_> { path, self.body.expr_path_hygiene(expr), ) - .map_or(true, |res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))), + .is_none_or(|res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))), Expr::Underscore => true, Expr::UnaryOp { op: UnaryOp::Deref, .. } => true, Expr::Field { .. } | Expr::Index { .. } => true, @@ -499,7 +499,7 @@ impl InferenceContext<'_> { // if the function is unresolved, we use is_varargs=true to // suppress the arg count diagnostic here let is_varargs = - derefed_callee.callable_sig(self.db).map_or(false, |sig| sig.is_varargs) + derefed_callee.callable_sig(self.db).is_some_and(|sig| sig.is_varargs) || res.is_none(); let (param_tys, ret_ty) = match res { Some((func, params, ret_ty)) => { @@ -531,7 +531,7 @@ impl InferenceContext<'_> { (params, ret_ty) } None => { - self.result.diagnostics.push(InferenceDiagnostic::ExpectedFunction { + self.push_diagnostic(InferenceDiagnostic::ExpectedFunction { call_expr: tgt_expr, found: callee_ty.clone(), }); @@ -707,7 +707,7 @@ impl InferenceContext<'_> { self.result.standard_types.never.clone() } Expr::RecordLit { path, fields, spread, .. } => { - let (ty, def_id) = self.resolve_variant(path.as_deref(), false); + let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path.as_deref(), false); if let Some(t) = expected.only_has_type(&mut self.table) { self.unify(&ty, &t); @@ -1816,9 +1816,10 @@ impl InferenceContext<'_> { if !is_public { if let Either::Left(field) = field_id { // FIXME: Merge this diagnostic into UnresolvedField? - self.result - .diagnostics - .push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field }); + self.push_diagnostic(InferenceDiagnostic::PrivateField { + expr: tgt_expr, + field, + }); } } ty @@ -1835,7 +1836,7 @@ impl InferenceContext<'_> { VisibleFromModule::Filter(self.resolver.module()), name, ); - self.result.diagnostics.push(InferenceDiagnostic::UnresolvedField { + self.push_diagnostic(InferenceDiagnostic::UnresolvedField { expr: tgt_expr, receiver: receiver_ty.clone(), name: name.clone(), @@ -1927,7 +1928,7 @@ impl InferenceContext<'_> { }, ); - self.result.diagnostics.push(InferenceDiagnostic::UnresolvedMethodCall { + self.push_diagnostic(InferenceDiagnostic::UnresolvedMethodCall { expr: tgt_expr, receiver: receiver_ty.clone(), name: method_name.clone(), @@ -2042,7 +2043,7 @@ impl InferenceContext<'_> { continue; } - while skip_indices.peek().map_or(false, |i| *i < idx as u32) { + while skip_indices.peek().is_some_and(|i| *i < idx as u32) { skip_indices.next(); } if skip_indices.peek().copied() == Some(idx as u32) { 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 50e761196ec1..00398f019da5 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 @@ -35,7 +35,7 @@ impl InferenceContext<'_> { ellipsis: Option, subs: &[PatId], ) -> Ty { - let (ty, def) = self.resolve_variant(path, true); + let (ty, def) = self.resolve_variant(id.into(), path, true); let var_data = def.map(|it| it.variant_data(self.db.upcast())); if let Some(variant) = def { self.write_variant_resolution(id.into(), variant); @@ -115,7 +115,7 @@ impl InferenceContext<'_> { id: PatId, subs: impl ExactSizeIterator, ) -> Ty { - let (ty, def) = self.resolve_variant(path, false); + let (ty, def) = self.resolve_variant(id.into(), path, false); if let Some(variant) = def { self.write_variant_resolution(id.into(), variant); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index a6296c3af233..73bcefaf2a9d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -14,6 +14,7 @@ use crate::{ builder::ParamKind, consteval, error_lifetime, generics::generics, + infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, method_resolution::{self, VisibleFromModule}, to_chalk_trait_id, InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId, @@ -147,36 +148,38 @@ impl InferenceContext<'_> { path: &Path, id: ExprOrPatId, ) -> Option<(ValueNs, Option>)> { + // Don't use `self.make_ty()` here as we need `orig_ns`. + let mut ctx = TyLoweringContext::new( + self.db, + &self.resolver, + &self.body.types, + self.owner.into(), + &self.diagnostics, + InferenceTyDiagnosticSource::Body, + ); let (value, self_subst) = if let Some(type_ref) = path.type_anchor() { let last = path.segments().last()?; - // Don't use `self.make_ty()` here as we need `orig_ns`. - let mut ctx = crate::lower::TyLoweringContext::new( - self.db, - &self.resolver, - &self.body.types, - self.owner.into(), - ); let (ty, orig_ns) = ctx.lower_ty_ext(type_ref); let ty = self.table.insert_type_vars(ty); let ty = self.table.normalize_associated_types_in(ty); let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty); - self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics); + drop(ctx); let ty = self.table.insert_type_vars(ty); let ty = self.table.normalize_associated_types_in(ty); self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? } else { let hygiene = self.body.expr_or_pat_path_hygiene(id); // FIXME: report error, unresolved first path segment - let value_or_partial = - self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, hygiene)?; + let value_or_partial = ctx.resolve_path_in_value_ns(path, id, hygiene)?; + drop(ctx); match value_or_partial { ResolveValueResult::ValueNs(it, _) => (it, None), ResolveValueResult::Partial(def, remaining_index, _) => self - .resolve_assoc_item(def, path, remaining_index, id) + .resolve_assoc_item(id, def, path, remaining_index, id) .map(|(it, substs)| (it, Some(substs)))?, } }; @@ -212,6 +215,7 @@ impl InferenceContext<'_> { fn resolve_assoc_item( &mut self, + node: ExprOrPatId, def: TypeNs, path: &Path, remaining_index: usize, @@ -260,17 +264,23 @@ impl InferenceContext<'_> { // as Iterator>::Item::default`) let remaining_segments_for_ty = remaining_segments.take(remaining_segments.len() - 1); - let (ty, _) = self.with_body_ty_lowering(|ctx| { - ctx.lower_partly_resolved_path( - def, - resolved_segment, - remaining_segments_for_ty, - true, - &mut |_, _reason| { - // FIXME: Report an error. - }, - ) - }); + let mut ctx = TyLoweringContext::new( + self.db, + &self.resolver, + &self.body.types, + self.owner.into(), + &self.diagnostics, + InferenceTyDiagnosticSource::Body, + ); + let (ty, _) = ctx.lower_partly_resolved_path( + node, + def, + resolved_segment, + remaining_segments_for_ty, + (remaining_index - 1) as u32, + true, + ); + drop(ctx); if ty.is_unknown() { return None; } @@ -305,13 +315,7 @@ impl InferenceContext<'_> { } AssocItemId::ConstId(konst) => { - if self - .db - .const_data(konst) - .name - .as_ref() - .map_or(false, |n| n == segment.name) - { + if self.db.const_data(konst).name.as_ref() == Some(segment.name) { Some(AssocItemId::ConstId(konst)) } else { None 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 165861c1b172..8a8992cf372d 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 @@ -282,7 +282,7 @@ impl<'a> InferenceTable<'a> { let is_diverging = self .type_variable_table .get(iv.index() as usize) - .map_or(false, |data| data.contains(TypeVariableFlags::DIVERGING)); + .is_some_and(|data| data.contains(TypeVariableFlags::DIVERGING)); if is_diverging { return TyKind::Never.intern(Interner); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs index 56b549436c70..d6039c548b6f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -139,7 +139,7 @@ impl UninhabitedFrom<'_> { ty: &Binders, subst: &Substitution, ) -> ControlFlow { - if vis.map_or(true, |it| it.is_visible_from(self.db.upcast(), self.target_mod)) { + if vis.is_none_or(|it| it.is_visible_from(self.db.upcast(), self.target_mod)) { let ty = ty.clone().substitute(Interner, subst); ty.visit_with(self, DebruijnIndex::INNERMOST) } else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs index f704b59d303e..ff9c52fbb6c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs @@ -13,6 +13,7 @@ pub fn is_box(db: &dyn HirDatabase, adt: AdtId) -> bool { pub fn is_unsafe_cell(db: &dyn HirDatabase, adt: AdtId) -> bool { let AdtId::StructId(id) = adt else { return false }; + db.struct_data(id).flags.contains(StructFlags::IS_UNSAFE_CELL) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index c1a67fcc4073..0ba765bd75ef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -113,7 +113,7 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound, let get = |name| { let attr = attrs.by_key(name).tt_values(); for tree in attr { - if let Some(it) = tree.token_trees.first() { + if let Some(it) = tree.iter().next_as_view() { let text = it.to_string().replace('_', ""); let (text, base) = match text.as_bytes() { [b'0', b'x', ..] => (&text[2..], 16), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index f40d508f755a..66159ddce2b1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -1,7 +1,7 @@ use chalk_ir::{AdtId, TyKind}; use either::Either; use hir_def::db::DefDatabase; -use project_model::{target_data_layout::RustcDataLayoutConfig, Sysroot}; +use project_model::{toolchain_info::QueryConfig, Sysroot}; use rustc_hash::FxHashMap; use syntax::ToSmolStr; use test_fixture::WithFixture; @@ -17,8 +17,8 @@ use crate::{ mod closure; fn current_machine_data_layout() -> String { - project_model::target_data_layout::get( - RustcDataLayoutConfig::Rustc(&Sysroot::empty()), + project_model::toolchain_info::target_data_layout::get( + QueryConfig::Rustc(&Sysroot::empty(), &std::env::current_dir().unwrap()), None, &FxHashMap::default(), ) @@ -241,31 +241,31 @@ fn recursive() { #[test] fn repr_packed() { size_and_align! { - #[repr(packed)] + #[repr(Rust, packed)] struct Goal; } size_and_align! { - #[repr(packed(2))] + #[repr(Rust, packed(2))] struct Goal; } size_and_align! { - #[repr(packed(4))] + #[repr(Rust, packed(4))] struct Goal; } size_and_align! { - #[repr(packed)] + #[repr(Rust, packed)] struct Goal(i32); } size_and_align! { - #[repr(packed(2))] + #[repr(Rust, packed(2))] struct Goal(i32); } size_and_align! { - #[repr(packed(4))] + #[repr(Rust, packed(4))] struct Goal(i32); } - check_size_and_align("#[repr(packed(5))] struct Goal(i32);", "", 4, 1); + check_size_and_align("#[repr(Rust, packed(5))] struct Goal(i32);", "", 4, 1); } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 8bb90ca31e47..3c18ea928165 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -24,7 +24,6 @@ extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis; mod builder; mod chalk_db; mod chalk_ext; -mod generics; mod infer; mod inhabitedness; mod interner; @@ -39,6 +38,7 @@ pub mod db; pub mod diagnostics; pub mod display; pub mod dyn_compatibility; +pub mod generics; pub mod lang_items; pub mod layout; pub mod method_resolution; @@ -50,6 +50,7 @@ pub mod traits; mod test_db; #[cfg(test)] mod tests; +mod variance; use std::hash::Hash; @@ -89,9 +90,8 @@ pub use infer::{ }; pub use interner::Interner; pub use lower::{ - associated_type_shorthand_candidates, GenericArgsProhibitedReason, ImplTraitLoweringMode, - ParamLoweringMode, TyDefId, TyLoweringContext, TyLoweringDiagnostic, TyLoweringDiagnosticKind, - ValueTyDefId, + associated_type_shorthand_candidates, diagnostics::*, ImplTraitLoweringMode, ParamLoweringMode, + TyDefId, TyLoweringContext, ValueTyDefId, }; pub use mapping::{ from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, @@ -101,6 +101,7 @@ pub use mapping::{ pub use method_resolution::check_orphan_rules; pub use traits::TraitEnvironment; pub use utils::{all_super_traits, direct_super_traits, is_fn_unsafe_to_call}; +pub use variance::Variance; pub use chalk_ir::{ cast::Cast, 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 b23f2749ab28..24f67fd66020 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -5,6 +5,8 @@ //! - Building the type for an item: This happens through the `ty` query. //! //! This usually involves resolving names, collecting generic arguments etc. +pub(crate) mod diagnostics; + use std::{ cell::OnceCell, iter, mem, @@ -21,8 +23,9 @@ use chalk_ir::{ use either::Either; use hir_def::{ + body::HygieneId, builtin_type::BuiltinType, - data::adt::StructKind, + data::{adt::StructKind, TraitFlags}, expander::Expander, generics::{ GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate, @@ -31,7 +34,7 @@ use hir_def::{ lang_item::LangItem, nameres::MacroSubNs, path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments}, - resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, + resolver::{HasResolver, LifetimeNs, ResolveValueResult, Resolver, TypeNs, ValueNs}, type_ref::{ ConstRef, LifetimeRef, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, TypesMap, TypesSourceMap, @@ -59,6 +62,7 @@ use crate::{ db::HirDatabase, error_lifetime, generics::{generics, trait_self_param_idx, Generics}, + lower::diagnostics::*, make_binders, mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk}, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, @@ -102,31 +106,6 @@ impl ImplTraitLoweringState { } } -type TypeSource = Either; - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct TyLoweringDiagnostic { - pub source: TypeSource, - pub kind: TyLoweringDiagnosticKind, -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum TyLoweringDiagnosticKind { - GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason }, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum GenericArgsProhibitedReason { - Module, - TyParam, - SelfTy, - PrimitiveTy, - /// When there is a generic enum, within the expression `Enum::Variant`, - /// either `Enum` or `Variant` are allowed to have generic arguments, but not both. - // FIXME: This is not used now but it should be. - EnumVariant, -} - #[derive(Debug)] pub struct TyLoweringContext<'a> { pub db: &'a dyn HirDatabase, @@ -536,8 +515,8 @@ impl<'a> TyLoweringContext<'a> { /// This is only for `generic_predicates_for_param`, where we can't just /// lower the self types of the predicates since that could lead to cycles. /// So we just check here if the `type_ref` resolves to a generic param, and which. - fn lower_ty_only_param(&self, type_ref: TypeRefId) -> Option { - let type_ref = &self.types_map[type_ref]; + fn lower_ty_only_param(&mut self, type_ref_id: TypeRefId) -> Option { + let type_ref = &self.types_map[type_ref_id]; let path = match type_ref { TypeRef::Path(path) => path, _ => return None, @@ -548,8 +527,10 @@ impl<'a> TyLoweringContext<'a> { if path.segments().len() > 1 { return None; } - let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { - Some((it, None, _)) => it, + let resolution = match self + .resolve_path_in_type_ns(path, &mut Self::on_path_diagnostic_callback(type_ref_id)) + { + Some((it, None)) => it, _ => return None, }; match resolution { @@ -584,11 +565,9 @@ impl<'a> TyLoweringContext<'a> { resolution: TypeNs, resolved_segment: PathSegment<'_>, remaining_segments: PathSegments<'_>, + _resolved_segment_idx: u32, infer_args: bool, - on_prohibited_generics_for_resolved_segment: &mut dyn FnMut( - &mut Self, - GenericArgsProhibitedReason, - ), + _on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic), ) -> (Ty, Option) { let ty = match resolution { TypeNs::TraitId(trait_) => { @@ -655,44 +634,28 @@ impl<'a> TyLoweringContext<'a> { // FIXME(trait_alias): Implement trait alias. return (TyKind::Error.intern(Interner), None); } - TypeNs::GenericParam(param_id) => { - if resolved_segment.args_and_bindings.is_some() { - on_prohibited_generics_for_resolved_segment( - self, - GenericArgsProhibitedReason::TyParam, - ); + TypeNs::GenericParam(param_id) => match self.type_param_mode { + ParamLoweringMode::Placeholder => { + TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into())) } + ParamLoweringMode::Variable => { + let idx = match self + .generics() + .expect("generics in scope") + .type_or_const_param_idx(param_id.into()) + { + None => { + never!("no matching generics"); + return (TyKind::Error.intern(Interner), None); + } + Some(idx) => idx, + }; - match self.type_param_mode { - ParamLoweringMode::Placeholder => { - TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into())) - } - ParamLoweringMode::Variable => { - let idx = match self - .generics() - .expect("generics in scope") - .type_or_const_param_idx(param_id.into()) - { - None => { - never!("no matching generics"); - return (TyKind::Error.intern(Interner), None); - } - Some(idx) => idx, - }; - - TyKind::BoundVar(BoundVar::new(self.in_binders, idx)) - } + TyKind::BoundVar(BoundVar::new(self.in_binders, idx)) } - .intern(Interner) } + .intern(Interner), TypeNs::SelfType(impl_id) => { - if resolved_segment.args_and_bindings.is_some() { - on_prohibited_generics_for_resolved_segment( - self, - GenericArgsProhibitedReason::SelfTy, - ); - } - let generics = self.generics().expect("impl should have generic param scope"); match self.type_param_mode { @@ -718,13 +681,6 @@ impl<'a> TyLoweringContext<'a> { } } TypeNs::AdtSelfType(adt) => { - if resolved_segment.args_and_bindings.is_some() { - on_prohibited_generics_for_resolved_segment( - self, - GenericArgsProhibitedReason::SelfTy, - ); - } - let generics = generics(self.db.upcast(), adt.into()); let substs = match self.type_param_mode { ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db), @@ -737,12 +693,6 @@ impl<'a> TyLoweringContext<'a> { TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args), TypeNs::BuiltinType(it) => { - if resolved_segment.args_and_bindings.is_some() { - on_prohibited_generics_for_resolved_segment( - self, - GenericArgsProhibitedReason::PrimitiveTy, - ); - } self.lower_path_inner(resolved_segment, it.into(), infer_args) } TypeNs::TypeAliasId(it) => { @@ -754,6 +704,220 @@ impl<'a> TyLoweringContext<'a> { self.lower_ty_relative_path(ty, Some(resolution), remaining_segments) } + fn handle_type_ns_resolution( + &mut self, + resolution: &TypeNs, + resolved_segment: PathSegment<'_>, + resolved_segment_idx: usize, + on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic), + ) { + let mut prohibit_generics_on_resolved = |reason| { + if resolved_segment.args_and_bindings.is_some() { + on_diagnostic( + self, + PathLoweringDiagnostic::GenericArgsProhibited { + segment: resolved_segment_idx as u32, + reason, + }, + ); + } + }; + + match resolution { + TypeNs::SelfType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + TypeNs::GenericParam(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam) + } + TypeNs::AdtSelfType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + TypeNs::BuiltinType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy) + } + TypeNs::AdtId(_) + | TypeNs::EnumVariantId(_) + | TypeNs::TypeAliasId(_) + | TypeNs::TraitId(_) + | TypeNs::TraitAliasId(_) => {} + } + } + + pub(crate) fn resolve_path_in_type_ns_fully( + &mut self, + path: &Path, + on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic), + ) -> Option { + let (res, unresolved) = self.resolve_path_in_type_ns(path, on_diagnostic)?; + if unresolved.is_some() { + return None; + } + Some(res) + } + + pub(crate) fn resolve_path_in_type_ns( + &mut self, + path: &Path, + on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic), + ) -> Option<(TypeNs, Option)> { + let (resolution, remaining_index, _) = + self.resolver.resolve_path_in_type_ns(self.db.upcast(), path)?; + let segments = path.segments(); + + match path { + // `segments.is_empty()` can occur with `self`. + Path::Normal(..) if !segments.is_empty() => (), + _ => return Some((resolution, remaining_index)), + }; + + let (module_segments, resolved_segment_idx, resolved_segment) = match remaining_index { + None => ( + segments.strip_last(), + segments.len() - 1, + segments.last().expect("resolved path has at least one element"), + ), + Some(i) => (segments.take(i - 1), i - 1, segments.get(i - 1).unwrap()), + }; + + for (i, mod_segment) in module_segments.iter().enumerate() { + if mod_segment.args_and_bindings.is_some() { + on_diagnostic( + self, + PathLoweringDiagnostic::GenericArgsProhibited { + segment: i as u32, + reason: GenericArgsProhibitedReason::Module, + }, + ); + } + } + + self.handle_type_ns_resolution( + &resolution, + resolved_segment, + resolved_segment_idx, + on_diagnostic, + ); + + Some((resolution, remaining_index)) + } + + pub(crate) fn resolve_path_in_value_ns( + &mut self, + path: &Path, + hygiene_id: HygieneId, + on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic), + ) -> Option { + let (res, prefix_info) = self.resolver.resolve_path_in_value_ns_with_prefix_info( + self.db.upcast(), + path, + hygiene_id, + )?; + + let segments = path.segments(); + match path { + // `segments.is_empty()` can occur with `self`. + Path::Normal(..) if !segments.is_empty() => (), + _ => return Some(res), + }; + + let (mod_segments, enum_segment) = match res { + ResolveValueResult::Partial(_, unresolved_segment, _) => { + (segments.take(unresolved_segment - 1), None) + } + ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _) + if prefix_info.enum_variant => + { + (segments.strip_last_two(), segments.len().checked_sub(2)) + } + ResolveValueResult::ValueNs(..) => (segments.strip_last(), None), + }; + for (i, mod_segment) in mod_segments.iter().enumerate() { + if mod_segment.args_and_bindings.is_some() { + on_diagnostic( + self, + PathLoweringDiagnostic::GenericArgsProhibited { + segment: i as u32, + reason: GenericArgsProhibitedReason::Module, + }, + ); + } + } + + if let Some(enum_segment) = enum_segment { + if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) + && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) + { + on_diagnostic( + self, + PathLoweringDiagnostic::GenericArgsProhibited { + segment: (enum_segment + 1) as u32, + reason: GenericArgsProhibitedReason::EnumVariant, + }, + ); + } + } + + match &res { + ResolveValueResult::ValueNs(resolution, _) => { + let resolved_segment_idx = + segments.len().checked_sub(1).unwrap_or_else(|| panic!("{path:?}")); + let resolved_segment = segments.last().unwrap(); + + let mut prohibit_generics_on_resolved = |reason| { + if resolved_segment.args_and_bindings.is_some() { + on_diagnostic( + self, + PathLoweringDiagnostic::GenericArgsProhibited { + segment: resolved_segment_idx as u32, + reason, + }, + ); + } + }; + + match resolution { + ValueNs::ImplSelf(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not + // E0109 (generic arguments provided for a type that doesn't accept them) for + // consts and statics, presumably as a defense against future in which consts + // and statics can be generic, or just because it was easier for rustc implementors. + // That means we'll show the wrong error code. Because of us it's easier to do it + // this way :) + ValueNs::GenericParam(_) | ValueNs::ConstId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const) + } + ValueNs::StaticId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static) + } + ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {} + ValueNs::LocalBinding(_) => {} + } + } + ResolveValueResult::Partial(resolution, unresolved_idx, _) => { + let resolved_segment_idx = unresolved_idx - 1; + let resolved_segment = segments.get(resolved_segment_idx).unwrap(); + self.handle_type_ns_resolution( + resolution, + resolved_segment, + resolved_segment_idx, + on_diagnostic, + ); + } + }; + Some(res) + } + + fn on_path_diagnostic_callback( + type_ref: TypeRefId, + ) -> impl FnMut(&mut Self, PathLoweringDiagnostic) { + move |this, diag| { + this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag)) + } + } + pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty, Option) { // Resolve the path (in type namespace) if let Some(type_ref) = path.type_anchor() { @@ -761,11 +925,13 @@ impl<'a> TyLoweringContext<'a> { return self.lower_ty_relative_path(ty, res, path.segments()); } - let (resolution, remaining_index, _) = - match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { - Some(it) => it, - None => return (TyKind::Error.intern(Interner), None), - }; + let (resolution, remaining_index) = match self.resolve_path_in_type_ns( + path, + &mut Self::on_path_diagnostic_callback(path_id.type_ref()), + ) { + Some(it) => it, + None => return (TyKind::Error.intern(Interner), None), + }; if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() { // trait object type without dyn @@ -774,38 +940,22 @@ impl<'a> TyLoweringContext<'a> { return (ty, None); } - let (module_segments, resolved_segment_idx, resolved_segment, remaining_segments) = - match remaining_index { - None => ( - path.segments().strip_last(), - path.segments().len() - 1, - path.segments().last().expect("resolved path has at least one element"), - PathSegments::EMPTY, - ), - Some(i) => ( - path.segments().take(i - 1), - i - 1, - path.segments().get(i - 1).unwrap(), - path.segments().skip(i), - ), - }; - - self.prohibit_generics(path_id, 0, module_segments, GenericArgsProhibitedReason::Module); + let (resolved_segment_idx, resolved_segment, remaining_segments) = match remaining_index { + None => ( + path.segments().len() - 1, + path.segments().last().expect("resolved path has at least one element"), + PathSegments::EMPTY, + ), + Some(i) => (i - 1, path.segments().get(i - 1).unwrap(), path.segments().skip(i)), + }; self.lower_partly_resolved_path( resolution, resolved_segment, remaining_segments, + resolved_segment_idx as u32, false, - &mut |this, reason| { - this.push_diagnostic( - path_id.type_ref(), - TyLoweringDiagnosticKind::GenericArgsProhibited { - segment: resolved_segment_idx as u32, - reason, - }, - ) - }, + &mut Self::on_path_diagnostic_callback(path_id.type_ref()), ) } @@ -1107,7 +1257,9 @@ impl<'a> TyLoweringContext<'a> { if segment.args_and_bindings.is_some() { self.push_diagnostic( path_id.type_ref(), - TyLoweringDiagnosticKind::GenericArgsProhibited { segment: idx, reason }, + TyLoweringDiagnosticKind::PathDiagnostic( + PathLoweringDiagnostic::GenericArgsProhibited { segment: idx, reason }, + ), ); } }); @@ -1119,7 +1271,10 @@ impl<'a> TyLoweringContext<'a> { explicit_self_ty: Ty, ) -> Option { let path = &self.types_map[path_id]; - let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? { + let resolved = match self.resolve_path_in_type_ns_fully( + path, + &mut Self::on_path_diagnostic_callback(path_id.type_ref()), + )? { // FIXME(trait_alias): We need to handle trait alias here. TypeNs::TraitId(tr) => tr, _ => return None, @@ -1412,9 +1567,17 @@ impl<'a> TyLoweringContext<'a> { match (lhs.skip_binders(), rhs.skip_binders()) { (WhereClause::Implemented(lhs), WhereClause::Implemented(rhs)) => { let lhs_id = lhs.trait_id; - let lhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(lhs_id)).is_auto; + let lhs_is_auto = ctx + .db + .trait_data(from_chalk_trait_id(lhs_id)) + .flags + .contains(TraitFlags::IS_AUTO); let rhs_id = rhs.trait_id; - let rhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(rhs_id)).is_auto; + let rhs_is_auto = ctx + .db + .trait_data(from_chalk_trait_id(rhs_id)) + .flags + .contains(TraitFlags::IS_AUTO); if !lhs_is_auto && !rhs_is_auto { multiple_regular_traits = true; @@ -2588,14 +2751,14 @@ fn fallback_bound_vars + HasInterner; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct TyLoweringDiagnostic { + pub source: TypeSource, + pub kind: TyLoweringDiagnosticKind, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum TyLoweringDiagnosticKind { + PathDiagnostic(PathLoweringDiagnostic), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum GenericArgsProhibitedReason { + Module, + TyParam, + SelfTy, + PrimitiveTy, + Const, + Static, + /// When there is a generic enum, within the expression `Enum::Variant`, + /// either `Enum` or `Variant` are allowed to have generic arguments, but not both. + // FIXME: This is not used now but it should be. + EnumVariant, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum PathLoweringDiagnostic { + GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason }, +} 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 5a72b97653db..62b071b2f326 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 @@ -7,7 +7,7 @@ use std::ops::ControlFlow; use base_db::CrateId; use chalk_ir::{cast::Cast, UniverseIndex, WithKind}; use hir_def::{ - data::{adt::StructFlags, ImplData}, + data::{adt::StructFlags, ImplData, TraitFlags}, nameres::DefMap, AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleId, TraitId, @@ -377,7 +377,7 @@ pub(crate) fn incoherent_inherent_impl_crates( for krate in crate_graph.transitive_deps(krate) { let impls = db.inherent_impls_in_crate(krate); - if impls.map.get(&fp).map_or(false, |v| !v.is_empty()) { + if impls.map.get(&fp).is_some_and(|v| !v.is_empty()) { res.push(krate); } } @@ -419,11 +419,17 @@ pub fn def_crates( } TyKind::Dyn(_) => { let trait_id = ty.dyn_trait()?; - Some(if db.trait_data(trait_id).rustc_has_incoherent_inherent_impls { - db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Dyn(trait_id)) - } else { - smallvec![trait_id.module(db.upcast()).krate()] - }) + Some( + if db + .trait_data(trait_id) + .flags + .contains(TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) + { + db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Dyn(trait_id)) + } else { + smallvec![trait_id.module(db.upcast()).krate()] + }, + ) } // for primitives, there may be impls in various places (core and alloc // mostly). We just check the whole crate graph for crates with impls @@ -805,8 +811,8 @@ fn is_inherent_impl_coherent( | TyKind::Scalar(_) => def_map.is_rustc_coherence_is_core(), &TyKind::Adt(AdtId(adt), _) => adt.module(db.upcast()).krate() == def_map.krate(), - TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| { - from_chalk_trait_id(trait_ref.trait_id).module(db.upcast()).krate() == def_map.krate() + TyKind::Dyn(it) => it.principal_id().is_some_and(|trait_id| { + from_chalk_trait_id(trait_id).module(db.upcast()).krate() == def_map.krate() }), _ => true, @@ -834,9 +840,10 @@ fn is_inherent_impl_coherent( .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL), hir_def::AdtId::EnumId(it) => db.enum_data(it).rustc_has_incoherent_inherent_impls, }, - TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| { - db.trait_data(from_chalk_trait_id(trait_ref.trait_id)) - .rustc_has_incoherent_inherent_impls + TyKind::Dyn(it) => it.principal_id().is_some_and(|trait_id| { + db.trait_data(from_chalk_trait_id(trait_id)) + .flags + .contains(TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS) }), _ => false, @@ -896,8 +903,8 @@ pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { match unwrap_fundamental(ty).kind(Interner) { &TyKind::Adt(AdtId(id), _) => is_local(id.module(db.upcast()).krate()), TyKind::Error => true, - TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| { - is_local(from_chalk_trait_id(trait_ref.trait_id).module(db.upcast()).krate()) + TyKind::Dyn(it) => it.principal_id().is_some_and(|trait_id| { + is_local(from_chalk_trait_id(trait_id).module(db.upcast()).krate()) }), _ => false, } @@ -914,7 +921,7 @@ pub fn iterate_path_candidates( traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, - callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>, + callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { iterate_method_candidates_dyn( ty, @@ -925,7 +932,7 @@ pub fn iterate_path_candidates( name, LookupMode::Path, // the adjustments are not relevant for path lookup - &mut |_, id, _| callback(id), + callback, ) } @@ -937,7 +944,7 @@ pub fn iterate_method_candidates_dyn( visible_from_module: VisibleFromModule, name: Option<&Name>, mode: LookupMode, - callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, + callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { let _p = tracing::info_span!( "iterate_method_candidates_dyn", @@ -1007,7 +1014,7 @@ fn iterate_method_candidates_with_autoref( traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, + callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { if receiver_ty.value.is_general_var(Interner, &receiver_ty.binders) { // don't try to resolve methods on unknown types @@ -1022,7 +1029,7 @@ fn iterate_method_candidates_with_autoref( traits_in_scope, visible_from_module, name, - &mut callback, + callback, ) }; @@ -1052,6 +1059,45 @@ fn iterate_method_candidates_with_autoref( iterate_method_candidates_by_receiver(ref_muted, first_adjustment.with_autoref(Mutability::Mut)) } +pub trait MethodCandidateCallback { + fn on_inherent_method( + &mut self, + adjustments: ReceiverAdjustments, + item: AssocItemId, + is_visible: bool, + ) -> ControlFlow<()>; + + fn on_trait_method( + &mut self, + adjustments: ReceiverAdjustments, + item: AssocItemId, + is_visible: bool, + ) -> ControlFlow<()>; +} + +impl MethodCandidateCallback for F +where + F: FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, +{ + fn on_inherent_method( + &mut self, + adjustments: ReceiverAdjustments, + item: AssocItemId, + is_visible: bool, + ) -> ControlFlow<()> { + self(adjustments, item, is_visible) + } + + fn on_trait_method( + &mut self, + adjustments: ReceiverAdjustments, + item: AssocItemId, + is_visible: bool, + ) -> ControlFlow<()> { + self(adjustments, item, is_visible) + } +} + #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_by_receiver( table: &mut InferenceTable<'_>, @@ -1060,7 +1106,7 @@ fn iterate_method_candidates_by_receiver( traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, + callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { let receiver_ty = table.instantiate_canonical(receiver_ty); // We're looking for methods with *receiver* type receiver_ty. These could @@ -1076,7 +1122,9 @@ fn iterate_method_candidates_by_receiver( Some(&receiver_ty), Some(receiver_adjustments.clone()), visible_from_module, - &mut callback, + &mut |adjustments, item, is_visible| { + callback.on_inherent_method(adjustments, item, is_visible) + }, )? } ControlFlow::Continue(()) @@ -1096,7 +1144,9 @@ fn iterate_method_candidates_by_receiver( name, Some(&receiver_ty), Some(receiver_adjustments.clone()), - &mut callback, + &mut |adjustments, item, is_visible| { + callback.on_trait_method(adjustments, item, is_visible) + }, )? } ControlFlow::Continue(()) @@ -1111,7 +1161,7 @@ fn iterate_method_candidates_for_self_ty( traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, + callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { let mut table = InferenceTable::new(db, env); let self_ty = table.instantiate_canonical(self_ty.clone()); @@ -1122,7 +1172,9 @@ fn iterate_method_candidates_for_self_ty( None, None, visible_from_module, - &mut callback, + &mut |adjustments, item, is_visible| { + callback.on_inherent_method(adjustments, item, is_visible) + }, )?; iterate_trait_method_candidates( &self_ty, @@ -1131,7 +1183,9 @@ fn iterate_method_candidates_for_self_ty( name, None, None, - callback, + &mut |adjustments, item, is_visible| { + callback.on_trait_method(adjustments, item, is_visible) + }, ) } @@ -1158,7 +1212,7 @@ fn iterate_trait_method_candidates( // 2021. // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for // arrays. - if data.skip_array_during_method_dispatch + if data.flags.contains(TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH) && matches!(self_ty.kind(Interner), TyKind::Array(..)) { // FIXME: this should really be using the edition of the method name's span, in case it @@ -1167,7 +1221,7 @@ fn iterate_trait_method_candidates( continue; } } - if data.skip_boxed_slice_during_method_dispatch + if data.flags.contains(TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH) && matches!( self_ty.kind(Interner), TyKind::Adt(AdtId(def), subst) if is_box(table.db, *def) @@ -1427,7 +1481,7 @@ fn is_valid_impl_method_candidate( AssocItemId::ConstId(c) => { let db = table.db; check_that!(receiver_ty.is_none()); - check_that!(name.map_or(true, |n| db.const_data(c).name.as_ref() == Some(n))); + check_that!(name.is_none_or(|n| db.const_data(c).name.as_ref() == Some(n))); if let Some(from_module) = visible_from_module { if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) { @@ -1465,7 +1519,7 @@ fn is_valid_trait_method_candidate( AssocItemId::FunctionId(fn_id) => { let data = db.function_data(fn_id); - check_that!(name.map_or(true, |n| n == &data.name)); + check_that!(name.is_none_or(|n| n == &data.name)); table.run_in_snapshot(|table| { let impl_subst = TyBuilder::subst_for_def(db, trait_id, None) @@ -1494,7 +1548,7 @@ fn is_valid_trait_method_candidate( } AssocItemId::ConstId(c) => { check_that!(receiver_ty.is_none()); - check_that!(name.map_or(true, |n| db.const_data(c).name.as_ref() == Some(n))); + check_that!(name.is_none_or(|n| db.const_data(c).name.as_ref() == Some(n))); IsValidCandidate::Yes } @@ -1515,7 +1569,7 @@ fn is_valid_impl_fn_candidate( let db = table.db; let data = db.function_data(fn_id); - check_that!(name.map_or(true, |n| n == &data.name)); + check_that!(name.is_none_or(|n| n == &data.name)); if let Some(from_module) = visible_from_module { if !db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module) { cov_mark::hit!(autoderef_candidate_not_visible); 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 e3072d6ee797..dcae6877ba80 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 @@ -1211,7 +1211,9 @@ impl Evaluator<'_> { } lc = &lc[..self.ptr_size()]; rc = &rc[..self.ptr_size()]; - ls + lc = self.read_memory(Address::from_bytes(lc)?, ls)?; + rc = self.read_memory(Address::from_bytes(rc)?, ls)?; + break 'binary_op Owned(vec![u8::from(lc == rc)]); } else { self.size_of_sized(&ty, locals, "operand of binary op")? }; @@ -1340,18 +1342,8 @@ impl Evaluator<'_> { } } else { let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_))); - let l128 = i128::from_le_bytes(pad16(lc, is_signed)); - let r128 = i128::from_le_bytes(pad16(rc, is_signed)); - let check_overflow = |r: i128| { - // FIXME: this is not very correct, and only catches the basic cases. - let r = r.to_le_bytes(); - for &k in &r[lc.len()..] { - if k != 0 && (k != 255 || !is_signed) { - return Err(MirEvalError::Panic(format!("Overflow in {op:?}"))); - } - } - Ok(Owned(r[0..lc.len()].into())) - }; + let l128 = IntValue::from_bytes(lc, is_signed); + let r128 = IntValue::from_bytes(rc, is_signed); match op { BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => { let r = op.run_compare(l128, r128) as u8; @@ -1366,25 +1358,31 @@ impl Evaluator<'_> { | BinOp::Rem | BinOp::Sub => { let r = match op { - BinOp::Add => l128.overflowing_add(r128).0, - BinOp::Mul => l128.overflowing_mul(r128).0, + BinOp::Add => l128.checked_add(r128).ok_or_else(|| { + MirEvalError::Panic(format!("Overflow in {op:?}")) + })?, + BinOp::Mul => l128.checked_mul(r128).ok_or_else(|| { + MirEvalError::Panic(format!("Overflow in {op:?}")) + })?, BinOp::Div => l128.checked_div(r128).ok_or_else(|| { MirEvalError::Panic(format!("Overflow in {op:?}")) })?, BinOp::Rem => l128.checked_rem(r128).ok_or_else(|| { MirEvalError::Panic(format!("Overflow in {op:?}")) })?, - BinOp::Sub => l128.overflowing_sub(r128).0, + BinOp::Sub => l128.checked_sub(r128).ok_or_else(|| { + MirEvalError::Panic(format!("Overflow in {op:?}")) + })?, BinOp::BitAnd => l128 & r128, BinOp::BitOr => l128 | r128, BinOp::BitXor => l128 ^ r128, _ => unreachable!(), }; - check_overflow(r)? + Owned(r.to_bytes()) } BinOp::Shl | BinOp::Shr => { let r = 'b: { - if let Ok(shift_amount) = u32::try_from(r128) { + if let Some(shift_amount) = r128.as_u32() { let r = match op { BinOp::Shl => l128.checked_shl(shift_amount), BinOp::Shr => l128.checked_shr(shift_amount), @@ -1401,7 +1399,7 @@ impl Evaluator<'_> { }; return Err(MirEvalError::Panic(format!("Overflow in {op:?}"))); }; - Owned(r.to_le_bytes()[..lc.len()].to_vec()) + Owned(r.to_bytes()) } BinOp::Offset => not_supported!("offset binop"), } @@ -2115,7 +2113,7 @@ impl Evaluator<'_> { while self.heap.len() % align != 0 { self.heap.push(0); } - if size.checked_add(self.heap.len()).map_or(true, |x| x > self.memory_limit) { + if size.checked_add(self.heap.len()).is_none_or(|x| x > self.memory_limit) { return Err(MirEvalError::Panic(format!("Memory allocation of {size} bytes failed"))); } let pos = self.heap.len(); @@ -2974,3 +2972,129 @@ pub fn pad16(it: &[u8], is_signed: bool) -> [u8; 16] { res[..it.len()].copy_from_slice(it); res } + +macro_rules! for_each_int_type { + ($call_macro:path, $args:tt) => { + $call_macro! { + $args + I8 + U8 + I16 + U16 + I32 + U32 + I64 + U64 + I128 + U128 + } + }; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +enum IntValue { + I8(i8), + U8(u8), + I16(i16), + U16(u16), + I32(i32), + U32(u32), + I64(i64), + U64(u64), + I128(i128), + U128(u128), +} + +macro_rules! checked_int_op { + ( [ $op:ident ] $( $int_ty:ident )+ ) => { + fn $op(self, other: Self) -> Option { + match (self, other) { + $( (Self::$int_ty(a), Self::$int_ty(b)) => a.$op(b).map(Self::$int_ty), )+ + _ => panic!("incompatible integer types"), + } + } + }; +} + +macro_rules! int_bit_shifts { + ( [ $op:ident ] $( $int_ty:ident )+ ) => { + fn $op(self, amount: u32) -> Option { + match self { + $( Self::$int_ty(this) => this.$op(amount).map(Self::$int_ty), )+ + } + } + }; +} + +macro_rules! unchecked_int_op { + ( [ $name:ident, $op:tt ] $( $int_ty:ident )+ ) => { + fn $name(self, other: Self) -> Self { + match (self, other) { + $( (Self::$int_ty(a), Self::$int_ty(b)) => Self::$int_ty(a $op b), )+ + _ => panic!("incompatible integer types"), + } + } + }; +} + +impl IntValue { + fn from_bytes(bytes: &[u8], is_signed: bool) -> Self { + match (bytes.len(), is_signed) { + (1, false) => Self::U8(u8::from_le_bytes(bytes.try_into().unwrap())), + (1, true) => Self::I8(i8::from_le_bytes(bytes.try_into().unwrap())), + (2, false) => Self::U16(u16::from_le_bytes(bytes.try_into().unwrap())), + (2, true) => Self::I16(i16::from_le_bytes(bytes.try_into().unwrap())), + (4, false) => Self::U32(u32::from_le_bytes(bytes.try_into().unwrap())), + (4, true) => Self::I32(i32::from_le_bytes(bytes.try_into().unwrap())), + (8, false) => Self::U64(u64::from_le_bytes(bytes.try_into().unwrap())), + (8, true) => Self::I64(i64::from_le_bytes(bytes.try_into().unwrap())), + (16, false) => Self::U128(u128::from_le_bytes(bytes.try_into().unwrap())), + (16, true) => Self::I128(i128::from_le_bytes(bytes.try_into().unwrap())), + _ => panic!("invalid integer size"), + } + } + + fn to_bytes(self) -> Vec { + macro_rules! m { + ( [] $( $int_ty:ident )+ ) => { + match self { + $( Self::$int_ty(v) => v.to_le_bytes().to_vec() ),+ + } + }; + } + for_each_int_type! { m, [] } + } + + fn as_u32(self) -> Option { + macro_rules! m { + ( [] $( $int_ty:ident )+ ) => { + match self { + $( Self::$int_ty(v) => v.try_into().ok() ),+ + } + }; + } + for_each_int_type! { m, [] } + } + + for_each_int_type!(checked_int_op, [checked_add]); + for_each_int_type!(checked_int_op, [checked_sub]); + for_each_int_type!(checked_int_op, [checked_div]); + for_each_int_type!(checked_int_op, [checked_rem]); + for_each_int_type!(checked_int_op, [checked_mul]); + + for_each_int_type!(int_bit_shifts, [checked_shl]); + for_each_int_type!(int_bit_shifts, [checked_shr]); +} + +impl std::ops::BitAnd for IntValue { + type Output = Self; + for_each_int_type!(unchecked_int_op, [bitand, &]); +} +impl std::ops::BitOr for IntValue { + type Output = Self; + for_each_int_type!(unchecked_int_op, [bitor, |]); +} +impl std::ops::BitXor for IntValue { + type Output = Self; + for_each_int_type!(unchecked_int_op, [bitxor, ^]); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 30d113737325..ce43e90df7d3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -879,3 +879,32 @@ fn main() { "#, ); } + +#[test] +fn long_str_eq_same_prefix() { + check_pass_and_stdio( + r#" +//- minicore: slice, index, coerce_unsized + +type pthread_key_t = u32; +type c_void = u8; +type c_int = i32; + +extern "C" { + pub fn write(fd: i32, buf: *const u8, count: usize) -> usize; +} + +fn main() { + // More than 16 bytes, the size of `i128`. + let long_str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"; + let output = match long_str { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => b"true" as &[u8], + _ => b"false", + }; + write(1, &output[0], output.len()); +} + "#, + "false", + "", + ); +} 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 cabeeea2bd86..b7607b5f6396 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -127,7 +127,15 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour None => continue, }; let def_map = module.def_map(&db); - visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it)); + visit_module(&db, &def_map, module.local_id, &mut |it| { + defs.push(match it { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }) + }); } defs.sort_by_key(|def| match def { DefWithBodyId::FunctionId(it) => { @@ -375,7 +383,15 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let def_map = module.def_map(&db); let mut defs: Vec = Vec::new(); - visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it)); + visit_module(&db, &def_map, module.local_id, &mut |it| { + defs.push(match it { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }) + }); defs.sort_by_key(|def| match def { DefWithBodyId::FunctionId(it) => { let loc = it.lookup(&db); @@ -405,11 +421,11 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { buf } -fn visit_module( +pub(crate) fn visit_module( db: &TestDB, crate_def_map: &DefMap, module_id: LocalModuleId, - cb: &mut dyn FnMut(DefWithBodyId), + cb: &mut dyn FnMut(ModuleDefId), ) { visit_scope(db, crate_def_map, &crate_def_map[module_id].scope, cb); for impl_id in crate_def_map[module_id].scope.impls() { @@ -417,18 +433,18 @@ fn visit_module( for &item in impl_data.items.iter() { match item { AssocItemId::FunctionId(it) => { - let def = it.into(); - cb(def); - let body = db.body(def); + let body = db.body(it.into()); + cb(it.into()); visit_body(db, &body, cb); } AssocItemId::ConstId(it) => { - let def = it.into(); - cb(def); - let body = db.body(def); + let body = db.body(it.into()); + cb(it.into()); visit_body(db, &body, cb); } - AssocItemId::TypeAliasId(_) => (), + AssocItemId::TypeAliasId(it) => { + cb(it.into()); + } } } } @@ -437,33 +453,27 @@ fn visit_module( db: &TestDB, crate_def_map: &DefMap, scope: &ItemScope, - cb: &mut dyn FnMut(DefWithBodyId), + cb: &mut dyn FnMut(ModuleDefId), ) { for decl in scope.declarations() { + cb(decl); match decl { ModuleDefId::FunctionId(it) => { - let def = it.into(); - cb(def); - let body = db.body(def); + let body = db.body(it.into()); visit_body(db, &body, cb); } ModuleDefId::ConstId(it) => { - let def = it.into(); - cb(def); - let body = db.body(def); + let body = db.body(it.into()); visit_body(db, &body, cb); } ModuleDefId::StaticId(it) => { - let def = it.into(); - cb(def); - let body = db.body(def); + let body = db.body(it.into()); visit_body(db, &body, cb); } ModuleDefId::AdtId(hir_def::AdtId::EnumId(it)) => { db.enum_data(it).variants.iter().for_each(|&(it, _)| { - let def = it.into(); - cb(def); - let body = db.body(def); + let body = db.body(it.into()); + cb(it.into()); visit_body(db, &body, cb); }); } @@ -473,7 +483,7 @@ fn visit_module( match item { AssocItemId::FunctionId(it) => cb(it.into()), AssocItemId::ConstId(it) => cb(it.into()), - AssocItemId::TypeAliasId(_) => (), + AssocItemId::TypeAliasId(it) => cb(it.into()), } } } @@ -483,7 +493,7 @@ fn visit_module( } } - fn visit_body(db: &TestDB, body: &Body, cb: &mut dyn FnMut(DefWithBodyId)) { + fn visit_body(db: &TestDB, body: &Body, cb: &mut dyn FnMut(ModuleDefId)) { for (_, def_map) in body.blocks(db) { for (mod_id, _) in def_map.modules() { visit_module(db, &def_map, mod_id, cb); @@ -553,7 +563,13 @@ fn salsa_bug() { let module = db.module_for_file(pos.file_id); let crate_def_map = module.def_map(&db); visit_module(&db, &crate_def_map, module.local_id, &mut |def| { - db.infer(def); + db.infer(match def { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }); }); let new_text = " @@ -586,6 +602,12 @@ fn salsa_bug() { let module = db.module_for_file(pos.file_id); let crate_def_map = module.def_map(&db); visit_module(&db, &crate_def_map, module.local_id, &mut |def| { - db.infer(def); + db.infer(match def { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }); }); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index b63d632dd26c..7de92d6b1607 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -24,6 +24,13 @@ fn check_closure_captures(ra_fixture: &str, expect: Expect) { let mut captures_info = Vec::new(); for def in defs { + let def = match def { + hir_def::ModuleDefId::FunctionId(it) => it.into(), + hir_def::ModuleDefId::EnumVariantId(it) => it.into(), + hir_def::ModuleDefId::ConstId(it) => it.into(), + hir_def::ModuleDefId::StaticId(it) => it.into(), + _ => continue, + }; let infer = db.infer(def); let db = &db; captures_info.extend(infer.closure_info.iter().flat_map(|(closure_id, (captures, _))| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 0a24eeb1fe82..3757d722ac83 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -1,4 +1,5 @@ use base_db::SourceDatabaseFileInputExt as _; +use hir_def::ModuleDefId; use test_fixture::WithFixture; use crate::{db::HirDatabase, test_db::TestDB}; @@ -19,7 +20,9 @@ fn foo() -> i32 { let module = db.module_for_file(pos.file_id.file_id()); let crate_def_map = module.def_map(&db); visit_module(&db, &crate_def_map, module.local_id, &mut |def| { - db.infer(def); + if let ModuleDefId::FunctionId(it) = def { + db.infer(it.into()); + } }); }); assert!(format!("{events:?}").contains("infer")) @@ -39,7 +42,9 @@ fn foo() -> i32 { let module = db.module_for_file(pos.file_id.file_id()); let crate_def_map = module.def_map(&db); visit_module(&db, &crate_def_map, module.local_id, &mut |def| { - db.infer(def); + if let ModuleDefId::FunctionId(it) = def { + db.infer(it.into()); + } }); }); assert!(!format!("{events:?}").contains("infer"), "{events:#?}") @@ -66,7 +71,9 @@ fn baz() -> i32 { let module = db.module_for_file(pos.file_id.file_id()); let crate_def_map = module.def_map(&db); visit_module(&db, &crate_def_map, module.local_id, &mut |def| { - db.infer(def); + if let ModuleDefId::FunctionId(it) = def { + db.infer(it.into()); + } }); }); assert!(format!("{events:?}").contains("infer")) @@ -91,7 +98,9 @@ fn baz() -> i32 { let module = db.module_for_file(pos.file_id.file_id()); let crate_def_map = module.def_map(&db); visit_module(&db, &crate_def_map, module.local_id, &mut |def| { - db.infer(def); + if let ModuleDefId::FunctionId(it) = def { + db.infer(it.into()); + } }); }); assert!(format!("{events:?}").matches("infer").count() == 1, "{events:#?}") diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 42e7edaf0f4f..bf7892f69bd3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -270,17 +270,15 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { return true; } - let is_intrinsic = db.attrs(func.into()).by_key(&sym::rustc_intrinsic).exists() - || data.abi.as_ref() == Some(&sym::rust_dash_intrinsic); - let loc = func.lookup(db.upcast()); match loc.container { hir_def::ItemContainerId::ExternBlockId(block) => { - if is_intrinsic || { - let id = block.lookup(db.upcast()).id; - id.item_tree(db.upcast())[id.value].abi.as_ref() == Some(&sym::rust_dash_intrinsic) - } { - // Intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute + let id = block.lookup(db.upcast()).id; + let is_intrinsic_block = + id.item_tree(db.upcast())[id.value].abi.as_ref() == Some(&sym::rust_dash_intrinsic); + if is_intrinsic_block { + // legacy intrinsics + // extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute !db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists() } else { // Function in an `extern` block are always unsafe to call, except when @@ -288,7 +286,6 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { !data.is_safe() } } - _ if is_intrinsic => !db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists(), _ => false, } } @@ -376,7 +373,7 @@ impl OpaqueInternableThing for InTypeConstIdMetadata { } fn dyn_eq(&self, other: &dyn OpaqueInternableThing) -> bool { - other.as_any().downcast_ref::().map_or(false, |x| self == x) + other.as_any().downcast_ref::() == Some(self) } fn dyn_clone(&self) -> Box { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs new file mode 100644 index 000000000000..30711b16dfb2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -0,0 +1,1065 @@ +//! Module for inferring the variance of type and lifetime parameters. See the [rustc dev guide] +//! chapter for more info. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/variance.html +//! +//! The implementation here differs from rustc. Rustc does a crate wide fixpoint resolution +//! as the algorithm for determining variance is a fixpoint computation with potential cycles that +//! need to be resolved. rust-analyzer does not want a crate-wide analysis though as that would hurt +//! incrementality too much and as such our query is based on a per item basis. +//! +//! This does unfortunately run into the issue that we can run into query cycles which salsa +//! currently does not allow to be resolved via a fixpoint computation. This will likely be resolved +//! by the next salsa version. If not, we will likely have to adapt and go with the rustc approach +//! while installing firewall per item queries to prevent invalidation issues. + +use crate::db::HirDatabase; +use crate::generics::{generics, Generics}; +use crate::{ + AliasTy, Const, ConstScalar, DynTyExt, GenericArg, GenericArgData, Interner, Lifetime, + LifetimeData, Ty, TyKind, +}; +use base_db::ra_salsa::Cycle; +use chalk_ir::Mutability; +use hir_def::data::adt::StructFlags; +use hir_def::{AdtId, GenericDefId, GenericParamId, VariantId}; +use std::fmt; +use std::ops::Not; +use stdx::never; +use triomphe::Arc; + +pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option> { + tracing::debug!("variances_of(def={:?})", def); + match def { + GenericDefId::FunctionId(_) => (), + GenericDefId::AdtId(adt) => { + if let AdtId::StructId(id) = adt { + let flags = &db.struct_data(id).flags; + if flags.contains(StructFlags::IS_UNSAFE_CELL) { + return Some(Arc::from_iter(vec![Variance::Invariant; 1])); + } else if flags.contains(StructFlags::IS_PHANTOM_DATA) { + return Some(Arc::from_iter(vec![Variance::Covariant; 1])); + } + } + } + _ => return None, + } + + let generics = generics(db.upcast(), def); + let count = generics.len(); + if count == 0 { + return None; + } + let variances = Context { generics, variances: vec![Variance::Bivariant; count], db }.solve(); + + variances.is_empty().not().then(|| Arc::from_iter(variances)) +} + +pub(crate) fn variances_of_cycle( + db: &dyn HirDatabase, + _cycle: &Cycle, + def: &GenericDefId, +) -> Option> { + let generics = generics(db.upcast(), *def); + let count = generics.len(); + + if count == 0 { + return None; + } + Some(Arc::from(vec![Variance::Bivariant; count])) +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum Variance { + Covariant, // T <: T iff A <: B -- e.g., function return type + Invariant, // T <: T iff B == A -- e.g., type of mutable cell + Contravariant, // T <: T iff B <: A -- e.g., function param type + Bivariant, // T <: T -- e.g., unused type parameter +} + +impl fmt::Display for Variance { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Variance::Covariant => write!(f, "covariant"), + Variance::Invariant => write!(f, "invariant"), + Variance::Contravariant => write!(f, "contravariant"), + Variance::Bivariant => write!(f, "bivariant"), + } + } +} + +impl Variance { + /// `a.xform(b)` combines the variance of a context with the + /// variance of a type with the following meaning. If we are in a + /// context with variance `a`, and we encounter a type argument in + /// a position with variance `b`, then `a.xform(b)` is the new + /// variance with which the argument appears. + /// + /// Example 1: + /// ```ignore (illustrative) + /// *mut Vec + /// ``` + /// Here, the "ambient" variance starts as covariant. `*mut T` is + /// invariant with respect to `T`, so the variance in which the + /// `Vec` appears is `Covariant.xform(Invariant)`, which + /// yields `Invariant`. Now, the type `Vec` is covariant with + /// respect to its type argument `T`, and hence the variance of + /// the `i32` here is `Invariant.xform(Covariant)`, which results + /// (again) in `Invariant`. + /// + /// Example 2: + /// ```ignore (illustrative) + /// fn(*const Vec, *mut Vec` appears is + /// `Contravariant.xform(Covariant)` or `Contravariant`. The same + /// is true for its `i32` argument. In the `*mut T` case, the + /// variance of `Vec` is `Contravariant.xform(Invariant)`, + /// and hence the outermost type is `Invariant` with respect to + /// `Vec` (and its `i32` argument). + /// + /// Source: Figure 1 of "Taming the Wildcards: + /// Combining Definition- and Use-Site Variance" published in PLDI'11. + fn xform(self, v: Variance) -> Variance { + match (self, v) { + // Figure 1, column 1. + (Variance::Covariant, Variance::Covariant) => Variance::Covariant, + (Variance::Covariant, Variance::Contravariant) => Variance::Contravariant, + (Variance::Covariant, Variance::Invariant) => Variance::Invariant, + (Variance::Covariant, Variance::Bivariant) => Variance::Bivariant, + + // Figure 1, column 2. + (Variance::Contravariant, Variance::Covariant) => Variance::Contravariant, + (Variance::Contravariant, Variance::Contravariant) => Variance::Covariant, + (Variance::Contravariant, Variance::Invariant) => Variance::Invariant, + (Variance::Contravariant, Variance::Bivariant) => Variance::Bivariant, + + // Figure 1, column 3. + (Variance::Invariant, _) => Variance::Invariant, + + // Figure 1, column 4. + (Variance::Bivariant, _) => Variance::Bivariant, + } + } + + fn glb(self, v: Variance) -> Variance { + // Greatest lower bound of the variance lattice as + // defined in The Paper: + // + // * + // - + + // o + match (self, v) { + (Variance::Invariant, _) | (_, Variance::Invariant) => Variance::Invariant, + + (Variance::Covariant, Variance::Contravariant) => Variance::Invariant, + (Variance::Contravariant, Variance::Covariant) => Variance::Invariant, + + (Variance::Covariant, Variance::Covariant) => Variance::Covariant, + + (Variance::Contravariant, Variance::Contravariant) => Variance::Contravariant, + + (x, Variance::Bivariant) | (Variance::Bivariant, x) => x, + } + } + + pub fn invariant(self) -> Self { + self.xform(Variance::Invariant) + } + + pub fn covariant(self) -> Self { + self.xform(Variance::Covariant) + } + + pub fn contravariant(self) -> Self { + self.xform(Variance::Contravariant) + } +} + +struct Context<'db> { + db: &'db dyn HirDatabase, + generics: Generics, + variances: Vec, +} + +impl Context<'_> { + fn solve(mut self) -> Vec { + tracing::debug!("solve(generics={:?})", self.generics); + match self.generics.def() { + GenericDefId::AdtId(adt) => { + let db = self.db; + let mut add_constraints_from_variant = |variant| { + let subst = self.generics.placeholder_subst(db); + for (_, field) in db.field_types(variant).iter() { + self.add_constraints_from_ty( + &field.clone().substitute(Interner, &subst), + Variance::Covariant, + ); + } + }; + match adt { + AdtId::StructId(s) => add_constraints_from_variant(VariantId::StructId(s)), + AdtId::UnionId(u) => add_constraints_from_variant(VariantId::UnionId(u)), + AdtId::EnumId(e) => { + db.enum_data(e).variants.iter().for_each(|&(variant, _)| { + add_constraints_from_variant(VariantId::EnumVariantId(variant)) + }); + } + } + } + GenericDefId::FunctionId(f) => { + let subst = self.generics.placeholder_subst(self.db); + self.add_constraints_from_sig( + self.db + .callable_item_signature(f.into()) + .substitute(Interner, &subst) + .params_and_return + .iter(), + Variance::Covariant, + ); + } + _ => {} + } + let mut variances = self.variances; + + // Const parameters are always invariant. + // Make all const parameters invariant. + for (idx, param) in self.generics.iter_id().enumerate() { + if let GenericParamId::ConstParamId(_) = param { + variances[idx] = Variance::Invariant; + } + } + + // Functions are permitted to have unused generic parameters: make those invariant. + if let GenericDefId::FunctionId(_) = self.generics.def() { + variances + .iter_mut() + .filter(|&&mut v| v == Variance::Bivariant) + .for_each(|v| *v = Variance::Invariant); + } + + variances + } + + /// Adds constraints appropriate for an instance of `ty` appearing + /// in a context with the generics defined in `generics` and + /// ambient variance `variance` + fn add_constraints_from_ty(&mut self, ty: &Ty, variance: Variance) { + tracing::debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance); + match ty.kind(Interner) { + TyKind::Scalar(_) | TyKind::Never | TyKind::Str | TyKind::Foreign(..) => { + // leaf type -- noop + } + TyKind::FnDef(..) | TyKind::Coroutine(..) | TyKind::Closure(..) => { + never!("Unexpected unnameable type in variance computation: {:?}", ty); + } + TyKind::Ref(mutbl, lifetime, ty) => { + self.add_constraints_from_region(lifetime, variance); + self.add_constraints_from_mt(ty, *mutbl, variance); + } + TyKind::Array(typ, len) => { + self.add_constraints_from_const(len, variance); + self.add_constraints_from_ty(typ, variance); + } + TyKind::Slice(typ) => { + self.add_constraints_from_ty(typ, variance); + } + TyKind::Raw(mutbl, ty) => { + self.add_constraints_from_mt(ty, *mutbl, variance); + } + TyKind::Tuple(_, subtys) => { + for subty in subtys.type_parameters(Interner) { + self.add_constraints_from_ty(&subty, variance); + } + } + TyKind::Adt(def, args) => { + self.add_constraints_from_args(def.0.into(), args.as_slice(Interner), variance); + } + TyKind::Alias(AliasTy::Opaque(opaque)) => { + self.add_constraints_from_invariant_args( + opaque.substitution.as_slice(Interner), + variance, + ); + } + TyKind::Alias(AliasTy::Projection(proj)) => { + self.add_constraints_from_invariant_args( + proj.substitution.as_slice(Interner), + variance, + ); + } + // FIXME: check this + TyKind::AssociatedType(_, subst) => { + self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); + } + // FIXME: check this + TyKind::OpaqueType(_, subst) => { + self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); + } + TyKind::Dyn(it) => { + // The type `dyn Trait +'a` is covariant w/r/t `'a`: + self.add_constraints_from_region(&it.lifetime, variance); + + if let Some(trait_ref) = it.principal() { + // Trait are always invariant so we can take advantage of that. + self.add_constraints_from_invariant_args( + trait_ref + .map(|it| it.map(|it| it.substitution.clone())) + .substitute( + Interner, + &[GenericArg::new( + Interner, + chalk_ir::GenericArgData::Ty(TyKind::Error.intern(Interner)), + )], + ) + .skip_binders() + .as_slice(Interner), + variance, + ); + } + + // FIXME + // for projection in data.projection_bounds() { + // match projection.skip_binder().term.unpack() { + // TyKind::TermKind::Ty(ty) => { + // self.add_constraints_from_ty( ty, self.invariant); + // } + // TyKind::TermKind::Const(c) => { + // self.add_constraints_from_const( c, self.invariant) + // } + // } + // } + } + + // Chalk has no params, so use placeholders for now? + TyKind::Placeholder(index) => { + let idx = crate::from_placeholder_idx(self.db, *index); + let index = self.generics.type_or_const_param_idx(idx).unwrap(); + self.constrain(index, variance); + } + TyKind::Function(f) => { + self.add_constraints_from_sig( + f.substitution.0.iter(Interner).filter_map(move |p| p.ty(Interner)), + variance, + ); + } + TyKind::Error => { + // we encounter this when walking the trait references for object + // types, where we use Error as the Self type + } + TyKind::CoroutineWitness(..) | TyKind::BoundVar(..) | TyKind::InferenceVar(..) => { + never!("unexpected type encountered in variance inference: {:?}", ty) + } + } + } + + fn add_constraints_from_invariant_args(&mut self, args: &[GenericArg], variance: Variance) { + let variance_i = variance.invariant(); + + for k in args { + match k.data(Interner) { + GenericArgData::Lifetime(lt) => self.add_constraints_from_region(lt, variance_i), + GenericArgData::Ty(ty) => self.add_constraints_from_ty(ty, variance_i), + GenericArgData::Const(val) => self.add_constraints_from_const(val, variance_i), + } + } + } + + /// Adds constraints appropriate for a nominal type (enum, struct, + /// object, etc) appearing in a context with ambient variance `variance` + fn add_constraints_from_args( + &mut self, + def_id: GenericDefId, + args: &[GenericArg], + variance: Variance, + ) { + // We don't record `inferred_starts` entries for empty generics. + if args.is_empty() { + return; + } + let Some(variances) = self.db.variances_of(def_id) else { + return; + }; + + for (i, k) in args.iter().enumerate() { + match k.data(Interner) { + GenericArgData::Lifetime(lt) => { + self.add_constraints_from_region(lt, variance.xform(variances[i])) + } + GenericArgData::Ty(ty) => { + self.add_constraints_from_ty(ty, variance.xform(variances[i])) + } + GenericArgData::Const(val) => self.add_constraints_from_const(val, variance), + } + } + } + + /// Adds constraints appropriate for a const expression `val` + /// in a context with ambient variance `variance` + fn add_constraints_from_const(&mut self, c: &Const, variance: Variance) { + match &c.data(Interner).value { + chalk_ir::ConstValue::Concrete(c) => { + if let ConstScalar::UnevaluatedConst(_, subst) = &c.interned { + self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); + } + } + _ => {} + } + } + + /// Adds constraints appropriate for a function with signature + /// `sig` appearing in a context with ambient variance `variance` + fn add_constraints_from_sig<'a>( + &mut self, + mut sig_tys: impl DoubleEndedIterator, + variance: Variance, + ) { + let contra = variance.contravariant(); + let Some(output) = sig_tys.next_back() else { + return never!("function signature has no return type"); + }; + self.add_constraints_from_ty(output, variance); + for input in sig_tys { + self.add_constraints_from_ty(input, contra); + } + } + + /// Adds constraints appropriate for a region appearing in a + /// context with ambient variance `variance` + fn add_constraints_from_region(&mut self, region: &Lifetime, variance: Variance) { + tracing::debug!( + "add_constraints_from_region(region={:?}, variance={:?})", + region, + variance + ); + match region.data(Interner) { + LifetimeData::Placeholder(index) => { + let idx = crate::lt_from_placeholder_idx(self.db, *index); + let inferred = self.generics.lifetime_idx(idx).unwrap(); + self.constrain(inferred, variance); + } + LifetimeData::Static => {} + LifetimeData::BoundVar(..) => { + // Either a higher-ranked region inside of a type or a + // late-bound function parameter. + // + // We do not compute constraints for either of these. + } + LifetimeData::Error => {} + LifetimeData::Phantom(..) | LifetimeData::InferenceVar(..) | LifetimeData::Erased => { + // We don't expect to see anything but 'static or bound + // regions when visiting member types or method types. + never!( + "unexpected region encountered in variance \ + inference: {:?}", + region + ); + } + } + } + + /// Adds constraints appropriate for a mutability-type pair + /// appearing in a context with ambient variance `variance` + fn add_constraints_from_mt(&mut self, ty: &Ty, mt: Mutability, variance: Variance) { + self.add_constraints_from_ty( + ty, + match mt { + Mutability::Mut => variance.invariant(), + Mutability::Not => variance, + }, + ); + } + + fn constrain(&mut self, index: usize, variance: Variance) { + tracing::debug!( + "constrain(index={:?}, variance={:?}, to={:?})", + index, + self.variances[index], + variance + ); + self.variances[index] = self.variances[index].glb(variance); + } +} + +#[cfg(test)] +mod tests { + use expect_test::{expect, Expect}; + use hir_def::{ + generics::GenericParamDataRef, src::HasSource, AdtId, GenericDefId, ModuleDefId, + }; + use itertools::Itertools; + use stdx::format_to; + use syntax::{ast::HasName, AstNode}; + use test_fixture::WithFixture; + + use hir_def::Lookup; + + use crate::{db::HirDatabase, test_db::TestDB, variance::generics}; + + #[test] + fn phantom_data() { + check( + r#" +//- minicore: phantom_data + +struct Covariant { + t: core::marker::PhantomData +} +"#, + expect![[r#" + Covariant[A: covariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_types() { + check( + r#" +//- minicore: cell + +use core::cell::UnsafeCell; + +struct InvariantMut<'a,A:'a,B:'a> { //~ ERROR ['a: +, A: o, B: o] + t: &'a mut (A,B) +} + +struct InvariantCell { //~ ERROR [A: o] + t: UnsafeCell +} + +struct InvariantIndirect { //~ ERROR [A: o] + t: InvariantCell +} + +struct Covariant { //~ ERROR [A: +] + t: A, u: fn() -> A +} + +struct Contravariant { //~ ERROR [A: -] + t: fn(A) +} + +enum Enum { //~ ERROR [A: +, B: -, C: o] + Foo(Covariant), + Bar(Contravariant),` + Zed(Covariant,Contravariant) +} +"#, + expect![[r#" + InvariantMut['a: covariant, A: invariant, B: invariant] + InvariantCell[A: invariant] + InvariantIndirect[A: invariant] + Covariant[A: covariant] + Contravariant[A: contravariant] + Enum[A: covariant, B: contravariant, C: invariant] + "#]], + ); + } + + #[test] + fn type_resolve_error_two_structs_deep() { + check( + r#" +struct Hello<'a> { + missing: Missing<'a>, +} + +struct Other<'a> { + hello: Hello<'a>, +} +"#, + expect![[r#" + Hello['a: bivariant] + Other['a: bivariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_associated_consts() { + // FIXME: Should be invariant + check( + r#" +trait Trait { + const Const: usize; +} + +struct Foo { //~ ERROR [T: o] + field: [u8; ::Const] +} +"#, + expect![[r#" + Foo[T: bivariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_associated_types() { + check( + r#" +trait Trait<'a> { + type Type; + + fn method(&'a self) { } +} + +struct Foo<'a, T : Trait<'a>> { //~ ERROR ['a: +, T: +] + field: (T, &'a ()) +} + +struct Bar<'a, T : Trait<'a>> { //~ ERROR ['a: o, T: o] + field: >::Type +} + +"#, + expect![[r#" + method[Self: contravariant, 'a: contravariant] + Foo['a: covariant, T: covariant] + Bar['a: invariant, T: invariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_associated_types2() { + // FIXME: RPITs have variance, but we can't treat them as their own thing right now + check( + r#" +trait Foo { + type Bar; +} + +fn make() -> *const dyn Foo {} +"#, + expect![""], + ); + } + + #[test] + fn rustc_test_variance_trait_bounds() { + check( + r#" +trait Getter { + fn get(&self) -> T; +} + +trait Setter { + fn get(&self, _: T); +} + +struct TestStruct> { //~ ERROR [U: +, T: +] + t: T, u: U +} + +enum TestEnum> { //~ ERROR [U: *, T: +] + //~^ ERROR: `U` is never used + Foo(T) +} + +struct TestContraStruct> { //~ ERROR [U: *, T: +] + //~^ ERROR: `U` is never used + t: T +} + +struct TestBox+Setter> { //~ ERROR [U: *, T: +] + //~^ ERROR: `U` is never used + t: T +} +"#, + expect![[r#" + get[Self: contravariant, T: covariant] + get[Self: contravariant, T: contravariant] + TestStruct[U: covariant, T: covariant] + TestEnum[U: bivariant, T: covariant] + TestContraStruct[U: bivariant, T: covariant] + TestBox[U: bivariant, T: covariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_trait_matching() { + check( + r#" + +trait Get { + fn get(&self) -> T; +} + +struct Cloner { + t: T +} + +impl Get for Cloner { + fn get(&self) -> T {} +} + +fn get<'a, G>(get: &G) -> i32 + where G : Get<&'a i32> +{} + +fn pick<'b, G>(get: &'b G, if_odd: &'b i32) -> i32 + where G : Get<&'b i32> +{} +"#, + expect![[r#" + get[Self: contravariant, T: covariant] + Cloner[T: covariant] + get[T: invariant] + get['a: invariant, G: contravariant] + pick['b: contravariant, G: contravariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_trait_object_bound() { + check( + r#" +enum Option { + Some(T), + None +} +trait T { fn foo(&self); } + +struct TOption<'a> { //~ ERROR ['a: +] + v: Option<*const (dyn T + 'a)>, +} +"#, + expect![[r#" + Option[T: covariant] + foo[Self: contravariant] + TOption['a: covariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_types_bounds() { + check( + r#" +//- minicore: send +struct TestImm { //~ ERROR [A: +, B: +] + x: A, + y: B, +} + +struct TestMut { //~ ERROR [A: +, B: o] + x: A, + y: &'static mut B, +} + +struct TestIndirect { //~ ERROR [A: +, B: o] + m: TestMut +} + +struct TestIndirect2 { //~ ERROR [A: o, B: o] + n: TestMut, + m: TestMut +} + +trait Getter { + fn get(&self) -> A; +} + +trait Setter { + fn set(&mut self, a: A); +} + +struct TestObject { //~ ERROR [A: o, R: o] + n: *const (dyn Setter + Send), + m: *const (dyn Getter + Send), +} +"#, + expect![[r#" + TestImm[A: covariant, B: covariant] + TestMut[A: covariant, B: invariant] + TestIndirect[A: covariant, B: invariant] + TestIndirect2[A: invariant, B: invariant] + get[Self: contravariant, A: covariant] + set[Self: invariant, A: contravariant] + TestObject[A: invariant, R: invariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_unused_region_param() { + check( + r#" +struct SomeStruct<'a> { x: u32 } //~ ERROR parameter `'a` is never used +enum SomeEnum<'a> { Nothing } //~ ERROR parameter `'a` is never used +trait SomeTrait<'a> { fn foo(&self); } // OK on traits. +"#, + expect![[r#" + SomeStruct['a: bivariant] + SomeEnum['a: bivariant] + foo[Self: contravariant, 'a: invariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_unused_type_param() { + check( + r#" +//- minicore: sized +struct SomeStruct { x: u32 } +enum SomeEnum { Nothing } +enum ListCell { + Cons(*const ListCell), + Nil +} + +struct SelfTyAlias(*const Self); +struct WithBounds {} +struct WithWhereBounds where T: Sized {} +struct WithOutlivesBounds {} +struct DoubleNothing { + s: SomeStruct, +} + +"#, + expect![[r#" + SomeStruct[A: bivariant] + SomeEnum[A: bivariant] + ListCell[T: bivariant] + SelfTyAlias[T: bivariant] + WithBounds[T: bivariant] + WithWhereBounds[T: bivariant] + WithOutlivesBounds[T: bivariant] + DoubleNothing[T: bivariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_use_contravariant_struct1() { + check( + r#" +struct SomeStruct(fn(T)); + +fn foo<'min,'max>(v: SomeStruct<&'max ()>) + -> SomeStruct<&'min ()> + where 'max : 'min +{} +"#, + expect![[r#" + SomeStruct[T: contravariant] + foo['min: contravariant, 'max: covariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_use_contravariant_struct2() { + check( + r#" +struct SomeStruct(fn(T)); + +fn bar<'min,'max>(v: SomeStruct<&'min ()>) + -> SomeStruct<&'max ()> + where 'max : 'min +{} +"#, + expect![[r#" + SomeStruct[T: contravariant] + bar['min: covariant, 'max: contravariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_use_covariant_struct1() { + check( + r#" +struct SomeStruct(T); + +fn foo<'min,'max>(v: SomeStruct<&'min ()>) + -> SomeStruct<&'max ()> + where 'max : 'min +{} +"#, + expect![[r#" + SomeStruct[T: covariant] + foo['min: contravariant, 'max: covariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_use_covariant_struct2() { + check( + r#" +struct SomeStruct(T); + +fn foo<'min,'max>(v: SomeStruct<&'max ()>) + -> SomeStruct<&'min ()> + where 'max : 'min +{} +"#, + expect![[r#" + SomeStruct[T: covariant] + foo['min: covariant, 'max: contravariant] + "#]], + ); + } + + #[test] + fn rustc_test_variance_use_invariant_struct1() { + check( + r#" +struct SomeStruct(*mut T); + +fn foo<'min,'max>(v: SomeStruct<&'max ()>) + -> SomeStruct<&'min ()> + where 'max : 'min +{} + +fn bar<'min,'max>(v: SomeStruct<&'min ()>) + -> SomeStruct<&'max ()> + where 'max : 'min +{} +"#, + expect![[r#" + SomeStruct[T: invariant] + foo['min: invariant, 'max: invariant] + bar['min: invariant, 'max: invariant] + "#]], + ); + } + + #[test] + fn invalid_arg_counts() { + check( + r#" +struct S(T); +struct S2(S<>); +struct S3(S); +"#, + expect![[r#" + S[T: covariant] + S2[T: bivariant] + S3[T: covariant] + "#]], + ); + } + + #[test] + fn prove_fixedpoint() { + // FIXME: This is wrong, this should be `FixedPoint[T: covariant, U: covariant, V: covariant]` + // This is a limitation of current salsa where a cycle may only set a fallback value to the + // query result, but we need to solve a fixpoint here. The new salsa will have this + // fortunately. + check( + r#" +struct FixedPoint(&'static FixedPoint<(), T, U>, V); +"#, + expect![[r#" + FixedPoint[T: bivariant, U: bivariant, V: bivariant] + "#]], + ); + } + + #[track_caller] + fn check(ra_fixture: &str, expected: Expect) { + // use tracing_subscriber::{layer::SubscriberExt, Layer}; + // let my_layer = tracing_subscriber::fmt::layer(); + // let _g = tracing::subscriber::set_default(tracing_subscriber::registry().with( + // my_layer.with_filter(tracing_subscriber::filter::filter_fn(|metadata| { + // metadata.target().starts_with("hir_ty::variance") + // })), + // )); + let (db, file_id) = TestDB::with_single_file(ra_fixture); + + let mut defs: Vec = Vec::new(); + let module = db.module_for_file_opt(file_id).unwrap(); + let def_map = module.def_map(&db); + crate::tests::visit_module(&db, &def_map, module.local_id, &mut |it| { + defs.push(match it { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::AdtId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::TraitId(it) => it.into(), + ModuleDefId::TraitAliasId(it) => it.into(), + ModuleDefId::TypeAliasId(it) => it.into(), + _ => return, + }) + }); + let defs = defs + .into_iter() + .filter_map(|def| { + Some(( + def, + match def { + GenericDefId::FunctionId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::AdtId(AdtId::EnumId(it)) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::AdtId(AdtId::StructId(it)) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::AdtId(AdtId::UnionId(it)) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::TraitId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::TraitAliasId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::TypeAliasId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::ImplId(_) => return None, + GenericDefId::ConstId(_) => return None, + }, + )) + }) + .sorted_by_key(|(_, n)| n.syntax().text_range().start()); + let mut res = String::new(); + for (def, name) in defs { + let Some(variances) = db.variances_of(def) else { + continue; + }; + format_to!( + res, + "{name}[{}]\n", + generics(&db, def) + .iter() + .map(|(_, param)| match param { + GenericParamDataRef::TypeParamData(type_param_data) => { + type_param_data.name.as_ref().unwrap() + } + GenericParamDataRef::ConstParamData(const_param_data) => + &const_param_data.name, + GenericParamDataRef::LifetimeParamData(lifetime_param_data) => { + &lifetime_param_data.name + } + }) + .zip_eq(&*variances) + .format_with(", ", |(name, var), f| f(&format_args!( + "{}: {var}", + name.as_str() + ))) + ); + } + + expected.assert_eq(&res); + } +} diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index af60c233e551..a23fdf1b3934 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -258,7 +258,7 @@ fn resolve_impl_trait_item( &traits_in_scope, method_resolution::VisibleFromModule::None, Some(name), - &mut |assoc_item_id| { + &mut |_, assoc_item_id: AssocItemId, _| { // If two traits in scope define the same item, Rustdoc links to no specific trait (for // instance, given two methods `a`, Rustdoc simply links to `method.a` with no // disambiguation) so we just pick the first one we find as well. diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index cbb1ed95ed64..fc77d1889c88 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -15,12 +15,12 @@ use hir_expand::{name::Name, HirFileId, InFile}; use hir_ty::{ db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, - CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic, - TyLoweringDiagnosticKind, + CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathLoweringDiagnostic, + TyLoweringDiagnostic, TyLoweringDiagnosticKind, }; use syntax::{ ast::{self, HasGenericArgs}, - AstPtr, SyntaxError, SyntaxNodePtr, TextRange, + match_ast, AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange, }; use triomphe::Arc; @@ -674,6 +674,39 @@ impl AnyDiagnostic { }; Self::ty_diagnostic(diag, source_map, db)? } + InferenceDiagnostic::PathDiagnostic { node, diag } => { + let source = expr_or_pat_syntax(*node)?; + let syntax = source.value.to_node(&db.parse_or_expand(source.file_id)); + let path = match_ast! { + match (syntax.syntax()) { + ast::RecordExpr(it) => it.path()?, + ast::RecordPat(it) => it.path()?, + ast::TupleStructPat(it) => it.path()?, + ast::PathExpr(it) => it.path()?, + ast::PathPat(it) => it.path()?, + _ => return None, + } + }; + Self::path_diagnostic(diag, source.with_value(path))? + } + }) + } + + fn path_diagnostic( + diag: &PathLoweringDiagnostic, + path: InFile, + ) -> Option { + Some(match diag { + &PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => { + let segment = hir_segment_to_ast_segment(&path.value, segment)?; + let args = if let Some(generics) = segment.generic_arg_list() { + AstPtr::new(&generics).wrap_left() + } else { + AstPtr::new(&segment.parenthesized_arg_list()?).wrap_right() + }; + let args = path.with_value(args); + GenericArgsProhibited { args, reason }.into() + } }) } @@ -693,17 +726,10 @@ impl AnyDiagnostic { Either::Right(source) => source, }; let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id)); - Some(match diag.kind { - TyLoweringDiagnosticKind::GenericArgsProhibited { segment, reason } => { + Some(match &diag.kind { + TyLoweringDiagnosticKind::PathDiagnostic(diag) => { let ast::Type::PathType(syntax) = syntax() else { return None }; - let segment = hir_segment_to_ast_segment(&syntax.path()?, segment)?; - let args = if let Some(generics) = segment.generic_arg_list() { - AstPtr::new(&generics).wrap_left() - } else { - AstPtr::new(&segment.parenthesized_arg_list()?).wrap_right() - }; - let args = source.with_value(args); - GenericArgsProhibited { args, reason }.into() + Self::path_diagnostic(diag, source.with_value(syntax.path()?))? } }) } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 959d62d59519..e09ded32fbdf 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -1,7 +1,10 @@ //! HirDisplay implementations for various hir types. use either::Either; use hir_def::{ - data::adt::{StructKind, VariantData}, + data::{ + adt::{StructKind, VariantData}, + TraitFlags, + }, generics::{ GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, @@ -22,7 +25,7 @@ use itertools::Itertools; use crate::{ Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, Macro, Module, - SelfParam, Static, Struct, Trait, TraitAlias, TupleField, TyBuilder, Type, TypeAlias, + SelfParam, Static, Struct, Trait, TraitAlias, TraitRef, TupleField, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant, }; @@ -743,6 +746,12 @@ impl HirDisplay for Static { } } +impl HirDisplay for TraitRef { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + self.trait_ref.hir_fmt(f) + } +} + impl HirDisplay for Trait { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_trait_header(self, f)?; @@ -785,10 +794,10 @@ impl HirDisplay for Trait { fn write_trait_header(trait_: &Trait, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(trait_.module(f.db).id, trait_.visibility(f.db), f)?; let data = f.db.trait_data(trait_.id); - if data.is_unsafe { + if data.flags.contains(TraitFlags::IS_UNSAFE) { f.write_str("unsafe ")?; } - if data.is_auto { + if data.flags.contains(TraitFlags::IS_AUTO) { f.write_str("auto ")?; } write!(f, "trait {}", data.name.display(f.db.upcast(), f.edition()))?; diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index dfc91c73433a..00b4db543740 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -43,7 +43,7 @@ use base_db::{CrateDisplayName, CrateId, CrateOrigin}; use either::Either; use hir_def::{ body::BodyDiagnostic, - data::adt::VariantData, + data::{adt::VariantData, TraitFlags}, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat}, item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode}, @@ -101,7 +101,6 @@ pub use crate::{ PathResolution, Semantics, SemanticsImpl, SemanticsScope, TypeInfo, VisibleTraits, }, }; -pub use hir_ty::method_resolution::TyFingerprint; // Be careful with these re-exports. // @@ -151,8 +150,9 @@ pub use { display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, dyn_compatibility::{DynCompatibilityViolation, MethodViolationCode}, layout::LayoutError, + method_resolution::TyFingerprint, mir::{MirEvalError, MirLowerError}, - CastError, FnAbi, PointerCast, Safety, + CastError, FnAbi, PointerCast, Safety, Variance, }, // FIXME: Properly encapsulate mir hir_ty::{mir, Interner as ChalkTyInterner}, @@ -699,7 +699,7 @@ impl Module { let source_map = tree_source_maps.impl_(loc.id.value).item(); let node = &tree[loc.id.value]; let file_id = loc.id.file_id(); - if file_id.macro_file().map_or(false, |it| it.is_builtin_derive(db.upcast())) { + if file_id.macro_file().is_some_and(|it| it.is_builtin_derive(db.upcast())) { // these expansion come from us, diagnosing them is a waste of resources // FIXME: Once we diagnose the inputs to builtin derives, we should at least extract those diagnostics somehow continue; @@ -724,7 +724,7 @@ impl Module { } let trait_ = impl_def.trait_(db); - let trait_is_unsafe = trait_.map_or(false, |t| t.is_unsafe(db)); + let trait_is_unsafe = trait_.is_some_and(|t| t.is_unsafe(db)); let impl_is_negative = impl_def.is_negative(db); let impl_is_unsafe = impl_def.is_unsafe(db); @@ -2778,11 +2778,11 @@ impl Trait { } pub fn is_auto(self, db: &dyn HirDatabase) -> bool { - db.trait_data(self.id).is_auto + db.trait_data(self.id).flags.contains(TraitFlags::IS_AUTO) } pub fn is_unsafe(&self, db: &dyn HirDatabase) -> bool { - db.trait_data(self.id).is_unsafe + db.trait_data(self.id).flags.contains(TraitFlags::IS_UNSAFE) } pub fn type_or_const_param_count( @@ -3574,6 +3574,61 @@ impl GenericDef { } } +// We cannot call this `Substitution` unfortunately... +#[derive(Debug)] +pub struct GenericSubstitution { + def: GenericDefId, + subst: Substitution, + env: Arc, +} + +impl GenericSubstitution { + fn new(def: GenericDefId, subst: Substitution, env: Arc) -> Self { + Self { def, subst, env } + } + + pub fn types(&self, db: &dyn HirDatabase) -> Vec<(Symbol, Type)> { + let container = match self.def { + GenericDefId::ConstId(id) => Some(id.lookup(db.upcast()).container), + GenericDefId::FunctionId(id) => Some(id.lookup(db.upcast()).container), + GenericDefId::TypeAliasId(id) => Some(id.lookup(db.upcast()).container), + _ => None, + }; + let container_type_params = container + .and_then(|container| match container { + ItemContainerId::ImplId(container) => Some(container.into()), + ItemContainerId::TraitId(container) => Some(container.into()), + _ => None, + }) + .map(|container| { + db.generic_params(container) + .iter_type_or_consts() + .filter_map(|param| match param.1 { + TypeOrConstParamData::TypeParamData(param) => Some(param.name.clone()), + TypeOrConstParamData::ConstParamData(_) => None, + }) + .collect::>() + }); + let generics = db.generic_params(self.def); + let type_params = generics.iter_type_or_consts().filter_map(|param| match param.1 { + TypeOrConstParamData::TypeParamData(param) => Some(param.name.clone()), + TypeOrConstParamData::ConstParamData(_) => None, + }); + // The `Substitution` is first self then container, we want the reverse order. + let self_params = self.subst.type_parameters(Interner).zip(type_params); + let container_params = self.subst.as_slice(Interner)[generics.len()..] + .iter() + .filter_map(|param| param.ty(Interner).cloned()) + .zip(container_type_params.into_iter().flatten()); + container_params + .chain(self_params) + .filter_map(|(ty, name)| { + Some((name?.symbol().clone(), Type { ty, env: self.env.clone() })) + }) + .collect() + } +} + /// A single local definition. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Local { @@ -3902,6 +3957,22 @@ impl GenericParam { GenericParam::LifetimeParam(it) => it.id.parent.into(), } } + + pub fn variance(self, db: &dyn HirDatabase) -> Option { + let parent = match self { + GenericParam::TypeParam(it) => it.id.parent(), + // const parameters are always invariant + GenericParam::ConstParam(_) => return None, + GenericParam::LifetimeParam(it) => it.id.parent, + }; + let generics = hir_ty::generics::generics(db.upcast(), parent); + let index = match self { + GenericParam::TypeParam(it) => generics.type_or_const_param_idx(it.id.into())?, + GenericParam::ConstParam(_) => return None, + GenericParam::LifetimeParam(it) => generics.lifetime_idx(it.id)?, + }; + db.variances_of(parent)?.get(index).copied() + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -5152,21 +5223,18 @@ impl Type { ) -> Option { let _p = tracing::info_span!("iterate_method_candidates_with_traits").entered(); let mut slot = None; - - self.iterate_method_candidates_dyn( + self.iterate_method_candidates_split_inherent( db, scope, traits_in_scope, with_local_impls, name, - &mut |assoc_item_id| { - if let AssocItemId::FunctionId(func) = assoc_item_id { - if let Some(res) = callback(func.into()) { - slot = Some(res); - return ControlFlow::Break(()); - } + |f| match callback(f) { + it @ Some(_) => { + slot = it; + ControlFlow::Break(()) } - ControlFlow::Continue(()) + None => ControlFlow::Continue(()), }, ); slot @@ -5190,15 +5258,49 @@ impl Type { ) } - fn iterate_method_candidates_dyn( + /// Allows you to treat inherent and non-inherent methods differently. + /// + /// Note that inherent methods may actually be trait methods! For example, in `dyn Trait`, the trait's methods + /// are considered inherent methods. + pub fn iterate_method_candidates_split_inherent( &self, db: &dyn HirDatabase, scope: &SemanticsScope<'_>, traits_in_scope: &FxHashSet, with_local_impls: Option, name: Option<&Name>, - callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>, + callback: impl MethodCandidateCallback, ) { + struct Callback(T); + + impl method_resolution::MethodCandidateCallback for Callback { + fn on_inherent_method( + &mut self, + _adjustments: method_resolution::ReceiverAdjustments, + item: AssocItemId, + _is_visible: bool, + ) -> ControlFlow<()> { + if let AssocItemId::FunctionId(func) = item { + self.0.on_inherent_method(func.into()) + } else { + ControlFlow::Continue(()) + } + } + + fn on_trait_method( + &mut self, + _adjustments: method_resolution::ReceiverAdjustments, + item: AssocItemId, + _is_visible: bool, + ) -> ControlFlow<()> { + if let AssocItemId::FunctionId(func) = item { + self.0.on_trait_method(func.into()) + } else { + ControlFlow::Continue(()) + } + } + } + let _p = tracing::info_span!( "iterate_method_candidates_dyn", with_local_impls = traits_in_scope.len(), @@ -5223,7 +5325,7 @@ impl Type { with_local_impls.and_then(|b| b.id.containing_block()).into(), name, method_resolution::LookupMode::MethodCall, - &mut |_adj, id, _| callback(id), + &mut Callback(callback), ); } @@ -5239,33 +5341,61 @@ impl Type { ) -> Option { let _p = tracing::info_span!("iterate_path_candidates").entered(); let mut slot = None; - self.iterate_path_candidates_dyn( + + self.iterate_path_candidates_split_inherent( db, scope, traits_in_scope, with_local_impls, name, - &mut |assoc_item_id| { - if let Some(res) = callback(assoc_item_id.into()) { - slot = Some(res); - return ControlFlow::Break(()); + |item| match callback(item) { + it @ Some(_) => { + slot = it; + ControlFlow::Break(()) } - ControlFlow::Continue(()) + None => ControlFlow::Continue(()), }, ); slot } + /// Iterates over inherent methods. + /// + /// In some circumstances, inherent methods methods may actually be trait methods! + /// For example, when `dyn Trait` is a receiver, _trait_'s methods would be considered + /// to be inherent methods. #[tracing::instrument(skip_all, fields(name = ?name))] - fn iterate_path_candidates_dyn( + pub fn iterate_path_candidates_split_inherent( &self, db: &dyn HirDatabase, scope: &SemanticsScope<'_>, traits_in_scope: &FxHashSet, with_local_impls: Option, name: Option<&Name>, - callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>, + callback: impl PathCandidateCallback, ) { + struct Callback(T); + + impl method_resolution::MethodCandidateCallback for Callback { + fn on_inherent_method( + &mut self, + _adjustments: method_resolution::ReceiverAdjustments, + item: AssocItemId, + _is_visible: bool, + ) -> ControlFlow<()> { + self.0.on_inherent_item(item.into()) + } + + fn on_trait_method( + &mut self, + _adjustments: method_resolution::ReceiverAdjustments, + item: AssocItemId, + _is_visible: bool, + ) -> ControlFlow<()> { + self.0.on_trait_item(item.into()) + } + } + let canonical = hir_ty::replace_errors_with_variables(&self.ty); let krate = scope.krate(); @@ -5281,7 +5411,7 @@ impl Type { traits_in_scope, with_local_impls.and_then(|b| b.id.containing_block()).into(), name, - callback, + &mut Callback(callback), ); } @@ -5862,6 +5992,12 @@ impl HasCrate for Adt { } } +impl HasCrate for Impl { + fn krate(&self, db: &dyn HirDatabase) -> Crate { + self.module(db).krate() + } +} + impl HasCrate for Module { fn krate(&self, _: &dyn HirDatabase) -> Crate { Module::krate(*self) @@ -5983,3 +6119,41 @@ fn push_ty_diagnostics( ); } } + +pub trait MethodCandidateCallback { + fn on_inherent_method(&mut self, f: Function) -> ControlFlow<()>; + + fn on_trait_method(&mut self, f: Function) -> ControlFlow<()>; +} + +impl MethodCandidateCallback for F +where + F: FnMut(Function) -> ControlFlow<()>, +{ + fn on_inherent_method(&mut self, f: Function) -> ControlFlow<()> { + self(f) + } + + fn on_trait_method(&mut self, f: Function) -> ControlFlow<()> { + self(f) + } +} + +pub trait PathCandidateCallback { + fn on_inherent_item(&mut self, item: AssocItem) -> ControlFlow<()>; + + fn on_trait_item(&mut self, item: AssocItem) -> ControlFlow<()>; +} + +impl PathCandidateCallback for F +where + F: FnMut(AssocItem) -> ControlFlow<()>, +{ + fn on_inherent_item(&mut self, item: AssocItem) -> ControlFlow<()> { + self(item) + } + + fn on_trait_item(&mut self, item: AssocItem) -> ControlFlow<()> { + self(item) + } +} diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 1cf22b05e7f4..34d169cd7612 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -30,7 +30,7 @@ use hir_expand::{ name::AsName, ExpandResult, FileRange, InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt, }; -use intern::Symbol; +use intern::{sym, Symbol}; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; @@ -49,10 +49,10 @@ use crate::{ semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{name_hygiene, resolve_hir_path, SourceAnalyzer}, Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, - ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile, - InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, - OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, - Type, TypeAlias, TypeParam, Union, Variant, VariantDef, + ConstParam, Crate, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource, + HirFileId, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, + Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait, + TraitAlias, TupleField, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, }; const CONTINUE_NO_BREAKS: ControlFlow = ControlFlow::Continue(()); @@ -246,59 +246,59 @@ impl Semantics<'_, DB> { } pub fn to_adt_def(&self, a: &ast::Adt) -> Option { - self.imp.to_def(a).map(Adt::from) + self.imp.to_def(a) } pub fn to_const_def(&self, c: &ast::Const) -> Option { - self.imp.to_def(c).map(Const::from) + self.imp.to_def(c) } pub fn to_enum_def(&self, e: &ast::Enum) -> Option { - self.imp.to_def(e).map(Enum::from) + self.imp.to_def(e) } pub fn to_enum_variant_def(&self, v: &ast::Variant) -> Option { - self.imp.to_def(v).map(Variant::from) + self.imp.to_def(v) } pub fn to_fn_def(&self, f: &ast::Fn) -> Option { - self.imp.to_def(f).map(Function::from) + self.imp.to_def(f) } pub fn to_impl_def(&self, i: &ast::Impl) -> Option { - self.imp.to_def(i).map(Impl::from) + self.imp.to_def(i) } pub fn to_macro_def(&self, m: &ast::Macro) -> Option { - self.imp.to_def(m).map(Macro::from) + self.imp.to_def(m) } pub fn to_module_def(&self, m: &ast::Module) -> Option { - self.imp.to_def(m).map(Module::from) + self.imp.to_def(m) } pub fn to_static_def(&self, s: &ast::Static) -> Option { - self.imp.to_def(s).map(Static::from) + self.imp.to_def(s) } pub fn to_struct_def(&self, s: &ast::Struct) -> Option { - self.imp.to_def(s).map(Struct::from) + self.imp.to_def(s) } pub fn to_trait_alias_def(&self, t: &ast::TraitAlias) -> Option { - self.imp.to_def(t).map(TraitAlias::from) + self.imp.to_def(t) } pub fn to_trait_def(&self, t: &ast::Trait) -> Option { - self.imp.to_def(t).map(Trait::from) + self.imp.to_def(t) } pub fn to_type_alias_def(&self, t: &ast::TypeAlias) -> Option { - self.imp.to_def(t).map(TypeAlias::from) + self.imp.to_def(t) } pub fn to_union_def(&self, u: &ast::Union) -> Option { - self.imp.to_def(u).map(Union::from) + self.imp.to_def(u) } } @@ -811,10 +811,37 @@ impl<'db> SemanticsImpl<'db> { item.attrs().any(|attr| { let Some(meta) = attr.meta() else { return false }; let Some(path) = meta.path() else { return false }; - let Some(attr_name) = path.as_single_name_ref() else { return true }; - let attr_name = attr_name.text(); - let attr_name = attr_name.as_str(); - attr_name == "derive" || find_builtin_attr_idx(&Symbol::intern(attr_name)).is_none() + if let Some(attr_name) = path.as_single_name_ref() { + let attr_name = attr_name.text(); + let attr_name = Symbol::intern(attr_name.as_str()); + if attr_name == sym::derive { + return true; + } + // We ignore `#[test]` and friends in the def map, so we cannot expand them. + // FIXME: We match by text. This is both hacky and incorrect (people can, and do, create + // other macros named `test`). We cannot fix that unfortunately because we use this method + // for speculative expansion in completion, which we cannot analyze. Fortunately, most macros + // named `test` are test-like, meaning their expansion is not terribly important for IDE. + if attr_name == sym::test + || attr_name == sym::bench + || attr_name == sym::test_case + || find_builtin_attr_idx(&attr_name).is_some() + { + return false; + } + } + let mut segments = path.segments(); + let mut next_segment_text = || segments.next().and_then(|it| it.name_ref()); + // `#[core::prelude::rust_2024::test]` or `#[std::prelude::rust_2024::test]`. + if next_segment_text().is_some_and(|it| matches!(&*it.text(), "core" | "std")) + && next_segment_text().is_some_and(|it| it.text() == "prelude") + && next_segment_text().is_some() + && next_segment_text() + .is_some_and(|it| matches!(&*it.text(), "test" | "bench" | "test_case")) + { + return false; + } + true }) }) } @@ -1206,7 +1233,6 @@ impl<'db> SemanticsImpl<'db> { node.original_file_range_opt(self.db.upcast()) .filter(|(_, ctx)| ctx.is_root()) .map(TupleExt::head) - .map(Into::into) } /// Attempts to map the node out of macro expanded files. @@ -1413,7 +1439,7 @@ impl<'db> SemanticsImpl<'db> { pub fn resolve_method_call_fallback( &self, call: &ast::MethodCallExpr, - ) -> Option> { + ) -> Option<(Either, Option)> { self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call) } @@ -1456,7 +1482,7 @@ impl<'db> SemanticsImpl<'db> { pub fn resolve_field_fallback( &self, field: &ast::FieldExpr, - ) -> Option, Function>> { + ) -> Option<(Either, Function>, Option)> { self.analyze(field.syntax())?.resolve_field_fallback(self.db, field) } @@ -1464,10 +1490,25 @@ impl<'db> SemanticsImpl<'db> { &self, field: &ast::RecordExprField, ) -> Option<(Field, Option, Type)> { + self.resolve_record_field_with_substitution(field) + .map(|(field, local, ty, _)| (field, local, ty)) + } + + pub fn resolve_record_field_with_substitution( + &self, + field: &ast::RecordExprField, + ) -> Option<(Field, Option, Type, GenericSubstitution)> { self.analyze(field.syntax())?.resolve_record_field(self.db, field) } pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> { + self.resolve_record_pat_field_with_subst(field).map(|(field, ty, _)| (field, ty)) + } + + pub fn resolve_record_pat_field_with_subst( + &self, + field: &ast::RecordPatField, + ) -> Option<(Field, Type, GenericSubstitution)> { self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field) } @@ -1485,7 +1526,7 @@ impl<'db> SemanticsImpl<'db> { pub fn is_proc_macro_call(&self, macro_call: &ast::MacroCall) -> bool { self.resolve_macro_call(macro_call) - .map_or(false, |m| matches!(m.id, MacroId::ProcMacroId(..))) + .is_some_and(|m| matches!(m.id, MacroId::ProcMacroId(..))) } pub fn resolve_macro_call_arm(&self, macro_call: &ast::MacroCall) -> Option { @@ -1523,6 +1564,13 @@ impl<'db> SemanticsImpl<'db> { } pub fn resolve_path(&self, path: &ast::Path) -> Option { + self.resolve_path_with_subst(path).map(|(it, _)| it) + } + + pub fn resolve_path_with_subst( + &self, + path: &ast::Path, + ) -> Option<(PathResolution, Option)> { self.analyze(path.syntax())?.resolve_path(self.db, path) } @@ -2038,6 +2086,11 @@ impl SemanticsScope<'_> { ) } + pub fn resolve_mod_path(&self, path: &ModPath) -> impl Iterator { + let items = self.resolver.resolve_module_path_in_items(self.db.upcast(), path); + items.iter_items().map(|(item, _)| item.into()) + } + /// Iterates over associated types that may be specified after the given path (using /// `Ty::Assoc` syntax). pub fn assoc_type_shorthand_candidates( diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 08333c2d76c0..b5cc440fc22e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -408,7 +408,7 @@ impl SourceToDefCtx<'_, '_> { } pub(super) fn has_derives(&mut self, adt: InFile<&ast::Adt>) -> bool { - self.dyn_map(adt).as_ref().map_or(false, |map| !map[keys::DERIVE_MACRO_CALL].is_empty()) + self.dyn_map(adt).as_ref().is_some_and(|map| !map[keys::DERIVE_MACRO_CALL].is_empty()) } fn to_def( 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 4329a888b392..b699ccde4128 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -9,8 +9,8 @@ use std::iter::{self, once}; use crate::{ db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr, - BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static, - Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant, + BuiltinType, Callable, Const, DeriveHelper, Field, Function, GenericSubstitution, Local, Macro, + ModuleDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant, }; use either::Either; use hir_def::{ @@ -18,15 +18,15 @@ use hir_def::{ scope::{ExprScopes, ScopeId}, Body, BodySourceMap, HygieneId, }, - hir::{BindingId, ExprId, ExprOrPatId, Pat, PatId}, + hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId}, lang_item::LangItem, lower::LowerCtx, nameres::MacroSubNs, path::{ModPath, Path, PathKind}, resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, type_ref::{Mutability, TypesMap, TypesSourceMap}, - AsMacroCall, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, - LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId, + AsMacroCall, AssocItemId, CallableDefId, ConstId, DefWithBodyId, FieldId, FunctionId, + ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId, }; use hir_expand::{ mod_path::path, @@ -38,9 +38,10 @@ use hir_ty::{ record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions, InsideUnsafeBlock, }, + from_assoc_type_id, lang_items::lang_items_for_bin_op, - method_resolution, Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, - TyLoweringContext, + method_resolution, Adjustment, InferenceResult, Interner, Substitution, TraitEnvironment, Ty, + TyExt, TyKind, TyLoweringContext, }; use intern::sym; use itertools::Itertools; @@ -120,6 +121,13 @@ impl SourceAnalyzer { self.def.as_ref().map(|(_, body, _)| &**body) } + fn trait_environment(&self, db: &dyn HirDatabase) -> Arc { + self.def.as_ref().map(|(def, ..)| *def).map_or_else( + || TraitEnvironment::empty(self.resolver.krate()), + |def| db.trait_environment_for_body(def), + ) + } + fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option { let src = match expr { ast::Expr::MacroExpr(expr) => { @@ -294,18 +302,23 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, call: &ast::MethodCallExpr, - ) -> Option> { + ) -> Option<(Either, Option)> { let expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?; let inference_result = self.infer.as_ref()?; match inference_result.method_resolution(expr_id) { - Some((f_in_trait, substs)) => Some(Either::Left( - self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into(), - )), - None => inference_result - .field_resolution(expr_id) - .and_then(Either::left) - .map(Into::into) - .map(Either::Right), + Some((f_in_trait, substs)) => { + let (fn_, subst) = + self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs); + Some(( + Either::Left(fn_.into()), + Some(GenericSubstitution::new(fn_.into(), subst, self.trait_environment(db))), + )) + } + None => { + inference_result.field_resolution(expr_id).and_then(Either::left).map(|field| { + (Either::Right(field.into()), self.field_subst(expr_id, inference_result, db)) + }) + } } } @@ -330,22 +343,53 @@ impl SourceAnalyzer { }) } + fn field_subst( + &self, + field_expr: ExprId, + infer: &InferenceResult, + db: &dyn HirDatabase, + ) -> Option { + let body = self.body()?; + if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] { + let (adt, subst) = type_of_expr_including_adjust(infer, object_expr)?.as_adt()?; + return Some(GenericSubstitution::new( + adt.into(), + subst.clone(), + self.trait_environment(db), + )); + } + None + } + pub(crate) fn resolve_field_fallback( &self, db: &dyn HirDatabase, field: &ast::FieldExpr, - ) -> Option, Function>> { + ) -> Option<(Either, Function>, Option)> { let &(def, ..) = self.def.as_ref()?; let expr_id = self.expr_id(db, &field.clone().into())?.as_expr()?; let inference_result = self.infer.as_ref()?; match inference_result.field_resolution(expr_id) { - Some(field) => Some(Either::Left(field.map_either(Into::into, |f| TupleField { - owner: def, - tuple: f.tuple, - index: f.index, - }))), + Some(field) => match field { + Either::Left(field) => Some(( + Either::Left(Either::Left(field.into())), + self.field_subst(expr_id, inference_result, db), + )), + Either::Right(field) => Some(( + Either::Left(Either::Right(TupleField { + owner: def, + tuple: field.tuple, + index: field.index, + })), + None, + )), + }, None => inference_result.method_resolution(expr_id).map(|(f, substs)| { - Either::Right(self.resolve_impl_method_or_trait_def(db, f, substs).into()) + let (f, subst) = self.resolve_impl_method_or_trait_def_with_subst(db, f, substs); + ( + Either::Right(f.into()), + Some(GenericSubstitution::new(f.into(), subst, self.trait_environment(db))), + ) }), } } @@ -557,7 +601,7 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, field: &ast::RecordExprField, - ) -> Option<(Field, Option, Type)> { + ) -> Option<(Field, Option, Type, GenericSubstitution)> { let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?; let expr = ast::Expr::from(record_expr); let expr_id = self.body_source_map()?.node_expr(InFile::new(self.file_id, &expr))?; @@ -583,30 +627,39 @@ impl SourceAnalyzer { _ => None, } }; - let (_, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?; + let (adt, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?; let variant = self.infer.as_ref()?.variant_resolution_for_expr_or_pat(expr_id)?; let variant_data = variant.variant_data(db.upcast()); let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? }; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); - Some((field.into(), local, Type::new_with_resolver(db, &self.resolver, field_ty))) + Some(( + field.into(), + local, + Type::new_with_resolver(db, &self.resolver, field_ty), + GenericSubstitution::new(adt.into(), subst.clone(), self.trait_environment(db)), + )) } pub(crate) fn resolve_record_pat_field( &self, db: &dyn HirDatabase, field: &ast::RecordPatField, - ) -> Option<(Field, Type)> { + ) -> Option<(Field, Type, GenericSubstitution)> { let field_name = field.field_name()?.as_name(); let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?; let pat_id = self.pat_id(&record_pat.into())?; let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?; let variant_data = variant.variant_data(db.upcast()); let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? }; - let (_, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?; + let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); - Some((field.into(), Type::new_with_resolver(db, &self.resolver, field_ty))) + Some(( + field.into(), + Type::new_with_resolver(db, &self.resolver, field_ty), + GenericSubstitution::new(adt.into(), subst.clone(), self.trait_environment(db)), + )) } pub(crate) fn resolve_macro_call( @@ -654,7 +707,7 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, path: &ast::Path, - ) -> Option { + ) -> Option<(PathResolution, Option)> { let parent = path.syntax().parent(); let parent = || parent.clone(); @@ -664,60 +717,106 @@ impl SourceAnalyzer { if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) { let expr_id = self.expr_id(db, &path_expr.into())?; if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr_or_pat(expr_id) { - let assoc = match assoc { + let (assoc, subst) = match assoc { AssocItemId::FunctionId(f_in_trait) => { match infer.type_of_expr_or_pat(expr_id) { - None => assoc, + None => { + let subst = GenericSubstitution::new( + f_in_trait.into(), + subs, + self.trait_environment(db), + ); + (assoc, subst) + } Some(func_ty) => { if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) { - self.resolve_impl_method_or_trait_def( - db, - f_in_trait, - subs.clone(), - ) - .into() + let (fn_, subst) = self + .resolve_impl_method_or_trait_def_with_subst( + db, + f_in_trait, + subs.clone(), + ); + let subst = GenericSubstitution::new( + fn_.into(), + subst, + self.trait_environment(db), + ); + (fn_.into(), subst) } else { - assoc + let subst = GenericSubstitution::new( + f_in_trait.into(), + subs, + self.trait_environment(db), + ); + (assoc, subst) } } } } AssocItemId::ConstId(const_id) => { - self.resolve_impl_const_or_trait_def(db, const_id, subs).into() + let (konst, subst) = + self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); + let subst = GenericSubstitution::new( + konst.into(), + subst, + self.trait_environment(db), + ); + (konst.into(), subst) } - assoc => assoc, + AssocItemId::TypeAliasId(type_alias) => ( + assoc, + GenericSubstitution::new( + type_alias.into(), + subs, + self.trait_environment(db), + ), + ), }; - return Some(PathResolution::Def(AssocItem::from(assoc).into())); + return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst))); } if let Some(VariantId::EnumVariantId(variant)) = infer.variant_resolution_for_expr_or_pat(expr_id) { - return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); + return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None)); } prefer_value_ns = true; } else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) { let pat_id = self.pat_id(&path_pat.into())?; if let Some((assoc, subs)) = infer.assoc_resolutions_for_pat(pat_id) { - let assoc = match assoc { + let (assoc, subst) = match assoc { AssocItemId::ConstId(const_id) => { - self.resolve_impl_const_or_trait_def(db, const_id, subs).into() + let (konst, subst) = + self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); + let subst = GenericSubstitution::new( + konst.into(), + subst, + self.trait_environment(db), + ); + (konst.into(), subst) } - assoc => assoc, + assoc => ( + assoc, + GenericSubstitution::new( + assoc.into(), + subs, + self.trait_environment(db), + ), + ), }; - return Some(PathResolution::Def(AssocItem::from(assoc).into())); + return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst))); } if let Some(VariantId::EnumVariantId(variant)) = infer.variant_resolution_for_pat(pat_id) { - return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); + return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None)); } } else if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) { let expr_id = self.expr_id(db, &rec_lit.into())?; if let Some(VariantId::EnumVariantId(variant)) = infer.variant_resolution_for_expr_or_pat(expr_id) { - return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); + return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None)); } } else { let record_pat = parent().and_then(ast::RecordPat::cast).map(ast::Pat::from); @@ -727,7 +826,10 @@ impl SourceAnalyzer { let pat_id = self.pat_id(&pat)?; let variant_res_for_pat = infer.variant_resolution_for_pat(pat_id); if let Some(VariantId::EnumVariantId(variant)) = variant_res_for_pat { - return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); + return Some(( + PathResolution::Def(ModuleDef::Variant(variant.into())), + None, + )); } } } @@ -747,7 +849,8 @@ impl SourceAnalyzer { // trying to resolve foo::bar. if let Some(use_tree) = parent().and_then(ast::UseTree::cast) { if use_tree.coloncolon_token().is_some() { - return resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map); + return resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) + .map(|it| (it, None)); } } @@ -765,13 +868,18 @@ impl SourceAnalyzer { // trying to resolve foo::bar. if path.parent_path().is_some() { return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) { - None if meta_path.is_some() => { - path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| { + None if meta_path.is_some() => path + .first_segment() + .and_then(|it| it.name_ref()) + .and_then(|name_ref| { ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) .map(PathResolution::ToolModule) }) - } - res => res, + .map(|it| (it, None)), + // FIXME: We do not show substitutions for parts of path, because this is really complex + // due to the interactions with associated items of `impl`s and associated items of associated + // types. + res => res.map(|it| (it, None)), }; } else if let Some(meta_path) = meta_path { // Case where we are resolving the final path segment of a path in an attribute @@ -781,7 +889,7 @@ impl SourceAnalyzer { let builtin = BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text()); if builtin.is_some() { - return builtin.map(PathResolution::BuiltinAttr); + return builtin.map(|it| (PathResolution::BuiltinAttr(it), None)); } if let Some(attr) = meta_path.parent_attr() { @@ -814,10 +922,13 @@ impl SourceAnalyzer { { if let Some(idx) = helpers.position(|(name, ..)| *name == name_ref) { - return Some(PathResolution::DeriveHelper(DeriveHelper { - derive: *macro_id, - idx: idx as u32, - })); + return Some(( + PathResolution::DeriveHelper(DeriveHelper { + derive: *macro_id, + idx: idx as u32, + }), + None, + )); } } } @@ -825,26 +936,79 @@ impl SourceAnalyzer { } } return match resolve_hir_path_as_attr_macro(db, &self.resolver, &hir_path) { - Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))), + Some(m) => Some((PathResolution::Def(ModuleDef::Macro(m)), None)), // this labels any path that starts with a tool module as the tool itself, this is technically wrong // but there is no benefit in differentiating these two cases for the time being - None => path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| { - ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) - .map(PathResolution::ToolModule) - }), + None => path + .first_segment() + .and_then(|it| it.name_ref()) + .and_then(|name_ref| { + ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) + .map(PathResolution::ToolModule) + }) + .map(|it| (it, None)), }; } - if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) { + if parent().is_some_and(|it| ast::Visibility::can_cast(it.kind())) { + // No substitution because only modules can be inside visibilities, and those have no generics. resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) + .map(|it| (it, None)) } else { - resolve_hir_path_( + // Probably a type, no need to show substitutions for those. + let res = resolve_hir_path_( db, &self.resolver, &hir_path, prefer_value_ns, name_hygiene(db, InFile::new(self.file_id, path.syntax())), &types_map, - ) + )?; + let subst = (|| { + let parent = parent()?; + let ty = if let Some(expr) = ast::Expr::cast(parent.clone()) { + let expr_id = self.expr_id(db, &expr)?; + self.infer.as_ref()?.type_of_expr_or_pat(expr_id)? + } else if let Some(pat) = ast::Pat::cast(parent) { + let pat_id = self.pat_id(&pat)?; + &self.infer.as_ref()?[pat_id] + } else { + return None; + }; + let env = self.trait_environment(db); + let (subst, expected_resolution) = match ty.kind(Interner) { + TyKind::Adt(adt_id, subst) => ( + GenericSubstitution::new(adt_id.0.into(), subst.clone(), env), + PathResolution::Def(ModuleDef::Adt(adt_id.0.into())), + ), + TyKind::AssociatedType(assoc_id, subst) => { + let assoc_id = from_assoc_type_id(*assoc_id); + ( + GenericSubstitution::new(assoc_id.into(), subst.clone(), env), + PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())), + ) + } + TyKind::FnDef(fn_id, subst) => { + let fn_id = hir_ty::db::InternedCallableDefId::from(*fn_id); + let fn_id = db.lookup_intern_callable_def(fn_id); + let generic_def_id = match fn_id { + CallableDefId::StructId(id) => id.into(), + CallableDefId::FunctionId(id) => id.into(), + CallableDefId::EnumVariantId(_) => return None, + }; + ( + GenericSubstitution::new(generic_def_id, subst.clone(), env), + PathResolution::Def(ModuleDefId::from(fn_id).into()), + ) + } + _ => return None, + }; + if res != expected_resolution { + // The user will not understand where we're coming from. This can happen (I think) with type aliases. + return None; + } + Some(subst) + })(); + Some((res, subst)) } } @@ -1041,26 +1205,35 @@ impl SourceAnalyzer { func: FunctionId, substs: Substitution, ) -> FunctionId { - let owner = match self.resolver.body_owner() { - Some(it) => it, - None => return func, - }; - let env = db.trait_environment_for_body(owner); - db.lookup_impl_method(env, func, substs).0 + self.resolve_impl_method_or_trait_def_with_subst(db, func, substs).0 } - fn resolve_impl_const_or_trait_def( + fn resolve_impl_method_or_trait_def_with_subst( + &self, + db: &dyn HirDatabase, + func: FunctionId, + substs: Substitution, + ) -> (FunctionId, Substitution) { + let owner = match self.resolver.body_owner() { + Some(it) => it, + None => return (func, substs), + }; + let env = db.trait_environment_for_body(owner); + db.lookup_impl_method(env, func, substs) + } + + fn resolve_impl_const_or_trait_def_with_subst( &self, db: &dyn HirDatabase, const_id: ConstId, subs: Substitution, - ) -> ConstId { + ) -> (ConstId, Substitution) { let owner = match self.resolver.body_owner() { Some(it) => it, - None => return const_id, + None => return (const_id, subs), }; let env = db.trait_environment_for_body(owner); - method_resolution::lookup_impl_const(db, env, const_id, subs).0 + method_resolution::lookup_impl_const(db, env, const_id, subs) } fn lang_trait_fn( @@ -1413,3 +1586,10 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H let ctx = db.lookup_intern_syntax_context(ctx); HygieneId::new(ctx.opaque_and_semitransparent) } + +fn type_of_expr_including_adjust(infer: &InferenceResult, id: ExprId) -> Option<&Ty> { + match infer.expr_adjustments.get(&id).and_then(|adjustments| adjustments.last()) { + Some(adjustment) => Some(&adjustment.target), + None => infer.type_of_expr.get(id), + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs index 12213c8455c7..43c0a72fa477 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs @@ -28,7 +28,7 @@ pub(crate) fn add_lifetime_to_type(acc: &mut Assists, ctx: &AssistContext<'_>) - let node = ctx.find_node_at_offset::()?; let has_lifetime = node .generic_param_list() - .map_or(false, |gen_list| gen_list.lifetime_params().next().is_some()); + .is_some_and(|gen_list| gen_list.lifetime_params().next().is_some()); if has_lifetime { return None; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 236d33878edc..24b34f140bd0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -220,7 +220,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .arms() .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_)))); if let Some(arm) = catch_all_arm { - let is_empty_expr = arm.expr().map_or(true, |e| match e { + let is_empty_expr = arm.expr().is_none_or(|e| match e { ast::Expr::BlockExpr(b) => { b.statements().next().is_none() && b.tail_expr().is_none() } @@ -261,7 +261,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) } if let Some(cap) = ctx.config.snippet_cap { - if let Some(it) = first_new_arm.and_then(|arm| arm.syntax().descendants().find_map(ast::WildcardPat::cast)) { + if let Some(it) = first_new_arm + .and_then(|arm| arm.syntax().descendants().find_map(ast::WildcardPat::cast)) + { edit.add_placeholder_snippet(cap, it); } @@ -287,14 +289,10 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) syntax::SyntaxElement::from(edit.make_syntax_mut(it)) } syntax::SyntaxElement::Token(it) => { - // Don't have a way to make tokens mut, so instead make the parent mut - // and find the token again - let parent = - edit.make_syntax_mut(it.parent().expect("Token must have a parent.")); - let mut_token = - parent.covering_element(it.text_range()).into_token().expect("Covering element cannot be found. Range may be beyond the current node's range"); - - syntax::SyntaxElement::from(mut_token) + // If a token is found, it is '{' or '}' + // The parent is `{ ... }` + let parent = it.parent().expect("Token must have a parent."); + syntax::SyntaxElement::from(edit.make_syntax_mut(parent)) } } }; 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 0f6970d9403e..62700ab1809f 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 @@ -69,7 +69,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti let ident = name_ref.ident_token()?; let def = match NameRefClass::classify(&ctx.sema, &name_ref)? { - NameRefClass::Definition(def) => def, + NameRefClass::Definition(def, _) => def, NameRefClass::FieldShorthand { .. } | NameRefClass::ExternCrateShorthand { .. } => { return None } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index c7f41ffce046..fd159eb824d6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -103,7 +103,7 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti ast::Expr::PathExpr(path) => { let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?; match NameRefClass::classify(&ctx.sema, &name_ref)? { - NameRefClass::Definition(Definition::Local(local)) => { + NameRefClass::Definition(Definition::Local(local), _) => { let source = local.sources(ctx.db()).into_iter().map(|x| x.into_ident_pat()?.name()); source.collect() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs index c30f3e1c3b2b..ea2752b88185 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs @@ -63,7 +63,7 @@ pub(crate) fn convert_nested_function_to_closure( /// Returns whether the given function is nested within the body of another function. fn is_nested_function(function: &ast::Fn) -> bool { - function.syntax().ancestors().skip(1).find_map(ast::Item::cast).map_or(false, |it| { + function.syntax().ancestors().skip(1).find_map(ast::Item::cast).is_some_and(|it| { matches!(it, ast::Item::Fn(_) | ast::Item::Static(_) | ast::Item::Const(_)) }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index 83f4a6b123c1..3c84f83906a9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -163,8 +163,8 @@ fn edit_struct_references( // this also includes method calls like Foo::new(42), we should skip them if let Some(name_ref) = path.segment().and_then(|s| s.name_ref()) { match NameRefClass::classify(&ctx.sema, &name_ref) { - Some(NameRefClass::Definition(Definition::SelfType(_))) => {}, - Some(NameRefClass::Definition(def)) if def == strukt_def => {}, + Some(NameRefClass::Definition(Definition::SelfType(_), _)) => {}, + Some(NameRefClass::Definition(def, _)) if def == strukt_def => {}, _ => return None, }; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index 22a1efdbea73..e34e50904875 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -96,8 +96,7 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option) -> Option, star: SyntaxToken) -> Option Some(def), _ => None, }) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 438769a0a875..0d1b6af72042 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -800,8 +800,8 @@ impl FunctionBody { let local_ref = match name_ref.and_then(|name_ref| NameRefClass::classify(sema, &name_ref)) { Some( - NameRefClass::Definition(Definition::Local(local_ref)) - | NameRefClass::FieldShorthand { local_ref, field_ref: _ }, + NameRefClass::Definition(Definition::Local(local_ref), _) + | NameRefClass::FieldShorthand { local_ref, field_ref: _, adt_subst: _ }, ) => local_ref, _ => return, }; @@ -856,7 +856,7 @@ impl FunctionBody { let mut set_parent_loop = |loop_: &dyn ast::HasLoopBody| { if loop_ .loop_body() - .map_or(false, |it| it.syntax().text_range().contains_range(self.text_range())) + .is_some_and(|it| it.syntax().text_range().contains_range(self.text_range())) { parent_loop.get_or_insert(loop_.syntax().clone()); } @@ -1090,7 +1090,7 @@ impl FunctionBody { let defined_outside_parent_loop = container_info .parent_loop .as_ref() - .map_or(true, |it| it.text_range().contains_range(src.syntax().text_range())); + .is_none_or(|it| it.text_range().contains_range(src.syntax().text_range())); let is_copy = ty.is_copy(ctx.db()); let has_usages = self.has_usages_after_body(&usages); 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 e4cba666af74..6e3be0ce6927 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 @@ -425,7 +425,9 @@ impl Module { }) } else if let Some(name_ref) = ast::NameRef::cast(x) { NameRefClass::classify(&ctx.sema, &name_ref).and_then(|nc| match nc { - NameRefClass::Definition(def) => Some((name_ref.syntax().clone(), def)), + NameRefClass::Definition(def, _) => { + Some((name_ref.syntax().clone(), def)) + } _ => None, }) } else { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs index 1eaf31628f31..67b8f5e50503 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -100,7 +100,7 @@ fn collect_used_generics<'gp>( fn find_lifetime(text: &str) -> impl Fn(&&ast::GenericParam) -> bool + '_ { move |gp: &&ast::GenericParam| match gp { ast::GenericParam::LifetimeParam(lp) => { - lp.lifetime().map_or(false, |lt| lt.text() == text) + lp.lifetime().is_some_and(|lt| lt.text() == text) } _ => false, } @@ -118,7 +118,7 @@ fn collect_used_generics<'gp>( ast::GenericParam::TypeParam(tp) => tp.name(), _ => None, } - .map_or(false, |n| n.text() == name_ref.text()) + .is_some_and(|n| n.text() == name_ref.text()) }) { generics.push(param); } @@ -165,7 +165,7 @@ fn collect_used_generics<'gp>( if let Some(name_ref) = path.as_single_name_ref() { if let Some(param) = known_generics.iter().find(|gp| { if let ast::GenericParam::ConstParam(cp) = gp { - cp.name().map_or(false, |n| n.text() == name_ref.text()) + cp.name().is_some_and(|n| n.text() == name_ref.text()) } else { false } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 6735d7dcbe10..0cc807aff642 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -99,7 +99,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let parent = to_extract.syntax().parent().and_then(ast::Expr::cast); // Any expression that autoderefs may need adjustment. - let mut needs_adjust = parent.as_ref().map_or(false, |it| match it { + let mut needs_adjust = parent.as_ref().is_some_and(|it| match it { ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) @@ -336,10 +336,12 @@ impl ExtractionKind { } } + let mut name_generator = + suggest_name::NameGenerator::new_from_scope_locals(ctx.sema.scope(to_extract.syntax())); let var_name = if let Some(literal_name) = get_literal_name(ctx, to_extract) { - literal_name + name_generator.suggest_name(&literal_name) } else { - suggest_name::for_variable(to_extract, &ctx.sema) + name_generator.for_variable(to_extract, &ctx.sema) }; let var_name = match self { @@ -352,10 +354,10 @@ impl ExtractionKind { } fn get_literal_name(ctx: &AssistContext<'_>, expr: &ast::Expr) -> Option { - let literal = match expr { - ast::Expr::Literal(literal) => literal, - _ => return None, + let ast::Expr::Literal(literal) = expr else { + return None; }; + let inner = match literal.kind() { ast::LiteralKind::String(string) => string.value().ok()?.into_owned(), ast::LiteralKind::ByteString(byte_string) => { @@ -2632,4 +2634,33 @@ fn foo() { "Extract into static", ); } + + #[test] + fn extract_variable_name_conflicts() { + check_assist_by_label( + extract_variable, + r#" +struct S { x: i32 }; + +fn main() { + let s = 2; + let t = $0S { x: 1 }$0; + let t2 = t; + let x = s; +} +"#, + r#" +struct S { x: i32 }; + +fn main() { + let s = 2; + let $0s1 = S { x: 1 }; + let t = s1; + let t2 = t; + let x = s; +} +"#, + "Extract into variable", + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs index 25076dd5255e..7f7db07152d3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs @@ -64,7 +64,7 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let name_ref_value = name_ref?; let name_ref_class = NameRefClass::classify(&ctx.sema, &name_ref_value); match name_ref_class { - Some(NameRefClass::Definition(Definition::Module(m))) => { + Some(NameRefClass::Definition(Definition::Module(m), _)) => { if !m.visibility(ctx.sema.db).is_visible_from(ctx.sema.db, constant_module.into()) { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs index f4b4c22d98d4..9d01ec00f836 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs @@ -85,7 +85,6 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) let is_unsafe = func_node.unsafe_token().is_some(); let ty = make::ty_fn_ptr( - None, is_unsafe, func_node.abi(), fn_params_vec.into_iter(), 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 7b95c124e62d..8f5daa4125a3 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 @@ -1077,7 +1077,7 @@ fn fn_arg_name(sema: &Semantics<'_, RootDatabase>, arg_expr: &ast::Expr) -> Stri .filter_map(ast::NameRef::cast) .filter(|name| name.ident_token().is_some()) .last()?; - if let Some(NameRefClass::Definition(Definition::Const(_) | Definition::Static(_))) = + if let Some(NameRefClass::Definition(Definition::Const(_) | Definition::Static(_), _)) = NameRefClass::classify(sema, &name_ref) { return Some(name_ref.to_string().to_lowercase()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs index 1dd376ac3fd5..5101d8fa0a9e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs @@ -78,7 +78,7 @@ pub(crate) fn move_bounds_to_where_clause( fn build_predicate(param: ast::TypeParam) -> Option { let path = make::ext::ident_path(¶m.name()?.syntax().to_string()); - let predicate = make::where_pred(path, param.type_bound_list()?.bounds()); + let predicate = make::where_pred(make::ty_path(path), param.type_bound_list()?.bounds()); Some(predicate.clone_for_update()) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs index 376243c26815..75120768da0f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs @@ -47,7 +47,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> .parent() // AssocItemList .and_then(|x| x.parent()) .and_then(ast::Impl::cast) - .map_or(false, |imp| imp.trait_().is_some()) + .is_some_and(|imp| imp.trait_().is_some()) { cov_mark::hit!(trait_impl); return None; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 56dd6cf29aef..b31d45e6d450 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -17,7 +17,7 @@ use syntax::{ }; use crate::{ - utils::{does_nested_pattern, does_pat_match_variant, unwrap_trivial_block}, + utils::{does_pat_match_variant, does_pat_variant_nested_or_literal, unwrap_trivial_block}, AssistContext, AssistId, AssistKind, Assists, }; @@ -135,7 +135,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' }; let has_preceding_if_expr = - if_expr.syntax().parent().map_or(false, |it| ast::IfExpr::can_cast(it.kind())); + if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind())); let expr = if has_preceding_if_expr { // make sure we replace the `else if let ...` with a block so we don't end up with `else expr` make::block_expr(None, Some(match_expr)).into() @@ -163,7 +163,7 @@ fn make_else_arm( Some(it) => { if does_pat_match_variant(pat, &it.sad_pattern()) { it.happy_pattern_wildcard() - } else if does_nested_pattern(pat) { + } else if does_pat_variant_nested_or_literal(ctx, pat) { make::wildcard_pat().into() } else { it.sad_pattern() @@ -241,7 +241,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' ast::Pat::LiteralPat(p) if p.literal() .map(|it| it.token().kind()) - .map_or(false, |it| it == T![true] || it == T![false]) => + .is_some_and(|it| it == T![true] || it == T![false]) => { "" } @@ -265,12 +265,12 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' let condition = match if_let_pat { ast::Pat::LiteralPat(p) - if p.literal().map_or(false, |it| it.token().kind() == T![true]) => + if p.literal().is_some_and(|it| it.token().kind() == T![true]) => { scrutinee } ast::Pat::LiteralPat(p) - if p.literal().map_or(false, |it| it.token().kind() == T![false]) => + if p.literal().is_some_and(|it| it.token().kind() == T![false]) => { make::expr_prefix(T![!], scrutinee) } @@ -339,10 +339,10 @@ fn binds_name(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool { ast::Pat::TupleStructPat(it) => it.fields().any(binds_name_v), ast::Pat::RecordPat(it) => it .record_pat_field_list() - .map_or(false, |rpfl| rpfl.fields().flat_map(|rpf| rpf.pat()).any(binds_name_v)), - ast::Pat::RefPat(pat) => pat.pat().map_or(false, binds_name_v), - ast::Pat::BoxPat(pat) => pat.pat().map_or(false, binds_name_v), - ast::Pat::ParenPat(pat) => pat.pat().map_or(false, binds_name_v), + .is_some_and(|rpfl| rpfl.fields().flat_map(|rpf| rpf.pat()).any(binds_name_v)), + ast::Pat::RefPat(pat) => pat.pat().is_some_and(binds_name_v), + ast::Pat::BoxPat(pat) => pat.pat().is_some_and(binds_name_v), + ast::Pat::ParenPat(pat) => pat.pat().is_some_and(binds_name_v), _ => false, } } @@ -350,7 +350,7 @@ fn binds_name(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool { fn is_sad_pat(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool { sema.type_of_pat(pat) .and_then(|ty| TryEnum::from_ty(sema, &ty.adjusted())) - .map_or(false, |it| does_pat_match_variant(pat, &it.sad_pattern())) + .is_some_and(|it| does_pat_match_variant(pat, &it.sad_pattern())) } #[cfg(test)] @@ -702,11 +702,11 @@ fn main() { } #[test] - fn nested_type() { + fn test_if_let_with_match_nested_tuple_struct() { check_assist( replace_if_let_with_match, r#" -//- minicore: result +//- minicore: result, option fn foo(x: Result) { let bar: Result<_, ()> = Ok(Some(1)); $0if let Ok(Some(_)) = bar { @@ -724,6 +724,700 @@ fn foo(x: Result) { _ => (), } } +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +struct MyStruct(i32, i32); +fn foo(x: Result) { + let bar: Result = Ok(MyStruct(1, 2)); + $0if let Ok(MyStruct(a, b)) = bar { + () + } else { + () + } +} +"#, + r#" +struct MyStruct(i32, i32); +fn foo(x: Result) { + let bar: Result = Ok(MyStruct(1, 2)); + match bar { + Ok(MyStruct(a, b)) => (), + Err(_) => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_slice() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<&[i32], ()>) { + let foo: Result<&[_], ()> = Ok(&[0, 1, 2]); + $0if let Ok([]) = foo { + () + } else { + () + } +} + "#, + r#" +fn foo(x: Result<&[i32], ()>) { + let foo: Result<&[_], ()> = Ok(&[0, 1, 2]); + match foo { + Ok([]) => (), + _ => (), + } +} + "#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<[&'static str; 2], ()>) { + let foobar: Result<_, ()> = Ok(["foo", "bar"]); + $0if let Ok([_, "bar"]) = foobar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<[&'static str; 2], ()>) { + let foobar: Result<_, ()> = Ok(["foo", "bar"]); + match foobar { + Ok([_, "bar"]) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<[&'static str; 2], ()>) { + let foobar: Result<_, ()> = Ok(["foo", "bar"]); + $0if let Ok([..]) = foobar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<[&'static str; 2], ()>) { + let foobar: Result<_, ()> = Ok(["foo", "bar"]); + match foobar { + Ok([..]) => (), + Err(_) => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<&[&'static str], ()>) { + let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]); + $0if let Ok([a, ..]) = foobar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<&[&'static str], ()>) { + let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]); + match foobar { + Ok([a, ..]) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<&[&'static str], ()>) { + let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]); + $0if let Ok([a, .., b, c]) = foobar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<&[&'static str], ()>) { + let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]); + match foobar { + Ok([a, .., b, c]) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result, ()>) { + let foobar: Result<_, ()> = Ok(Some(["foo", "bar"])); + $0if let Ok(Some([_, "bar"])) = foobar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result, ()>) { + let foobar: Result<_, ()> = Ok(Some(["foo", "bar"])); + match foobar { + Ok(Some([_, "bar"])) => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_literal() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<&'static str, ()>) { + let bar: Result<&_, ()> = Ok("bar"); + $0if let Ok("foo") = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<&'static str, ()>) { + let bar: Result<&_, ()> = Ok("bar"); + match bar { + Ok("foo") => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_tuple() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<(i32, i32, i32), ()>) { + let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3)); + $0if let Ok((1, second, third)) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<(i32, i32, i32), ()>) { + let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3)); + match bar { + Ok((1, second, third)) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<(i32, i32, i32), ()>) { + let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3)); + $0if let Ok((first, second, third)) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<(i32, i32, i32), ()>) { + let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3)); + match bar { + Ok((first, second, third)) => (), + Err(_) => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_or() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result) { + let bar: Result = Ok(1); + $0if let Ok(1 | 2) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result) { + let bar: Result = Ok(1); + match bar { + Ok(1 | 2) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 2)); + $0if let Ok((b, a) | (a, b)) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 2)); + match bar { + Ok((b, a) | (a, b)) => (), + Err(_) => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 2)); + $0if let Ok((1, a) | (a, 2)) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 2)); + match bar { + Ok((1, a) | (a, 2)) => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_range() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result) { + let bar: Result = Ok(1); + $0if let Ok(1..2) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result) { + let bar: Result = Ok(1); + match bar { + Ok(1..2) => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_paren() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 1)); + $0if let Ok(((1, 2))) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 1)); + match bar { + Ok(((1, 2))) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 1)); + $0if let Ok(((a, b))) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<(i32, i32), ()>) { + let bar: Result<(i32, i32), ()> = Ok((1, 1)); + match bar { + Ok(((a, b))) => (), + Err(_) => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_macro() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result) { + macro_rules! is_42 { + () => { + 42 + }; + } + + let bar: Result = Ok(1); + $0if let Ok(is_42!()) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result) { + macro_rules! is_42 { + () => { + 42 + }; + } + + let bar: Result = Ok(1); + match bar { + Ok(is_42!()) => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_path() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +enum MyEnum { + Foo, + Bar, +} + +fn foo(x: Result) { + let bar: Result = Ok(MyEnum::Foo); + $0if let Ok(MyEnum::Foo) = bar { + () + } else { + () + } +} +"#, + r#" +enum MyEnum { + Foo, + Bar, +} + +fn foo(x: Result) { + let bar: Result = Ok(MyEnum::Foo); + match bar { + Ok(MyEnum::Foo) => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_record() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +struct MyStruct { + foo: i32, + bar: i32, +} + +fn foo(x: Result) { + let bar: Result = Ok(MyStruct { foo: 1, bar: 2 }); + $0if let Ok(MyStruct { foo, bar }) = bar { + () + } else { + () + } +} +"#, + r#" +struct MyStruct { + foo: i32, + bar: i32, +} + +fn foo(x: Result) { + let bar: Result = Ok(MyStruct { foo: 1, bar: 2 }); + match bar { + Ok(MyStruct { foo, bar }) => (), + Err(_) => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +struct MyStruct { + foo: i32, + bar: i32, +} + +fn foo(x: Result) { + let bar: Result = Ok(MyStruct { foo: 1, bar: 2 }); + $0if let Ok(MyStruct { foo, bar: 12 }) = bar { + () + } else { + () + } +} +"#, + r#" +struct MyStruct { + foo: i32, + bar: i32, +} + +fn foo(x: Result) { + let bar: Result = Ok(MyStruct { foo: 1, bar: 2 }); + match bar { + Ok(MyStruct { foo, bar: 12 }) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +struct MyStruct { + foo: i32, + bar: i32, +} + +fn foo(x: Result) { + let bar: Result = Ok(MyStruct { foo: 1, bar: 2 }); + $0if let Ok(MyStruct { foo, .. }) = bar { + () + } else { + () + } +} +"#, + r#" +struct MyStruct { + foo: i32, + bar: i32, +} + +fn foo(x: Result) { + let bar: Result = Ok(MyStruct { foo: 1, bar: 2 }); + match bar { + Ok(MyStruct { foo, .. }) => (), + Err(_) => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +enum MyEnum { + Foo(i32, i32), + Bar { a: i32, b: i32 }, +} + +fn foo(x: Result) { + let bar: Result = Ok(MyEnum::Foo(1, 2)); + $0if let Ok(MyEnum::Bar { a, b }) = bar { + () + } else { + () + } +} +"#, + r#" +enum MyEnum { + Foo(i32, i32), + Bar { a: i32, b: i32 }, +} + +fn foo(x: Result) { + let bar: Result = Ok(MyEnum::Foo(1, 2)); + match bar { + Ok(MyEnum::Bar { a, b }) => (), + _ => (), + } +} +"#, + ); + } + + #[test] + fn test_if_let_with_match_nested_ident() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result) { + let bar: Result = Ok(1); + $0if let Ok(a @ 1..2) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result) { + let bar: Result = Ok(1); + match bar { + Ok(a @ 1..2) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result) { + let bar: Result = Ok(1); + $0if let Ok(a) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result) { + let bar: Result = Ok(1); + match bar { + Ok(a) => (), + Err(_) => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result +fn foo(x: Result) { + let bar: Result = Ok(1); + $0if let Ok(a @ b @ c @ d) = bar { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result) { + let bar: Result = Ok(1); + match bar { + Ok(a @ b @ c @ d) => (), + Err(_) => (), + } +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index a856da092153..47972ff619ac 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -20,7 +20,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // ``` // fn main() { // let x = Some(1); -// if let Some(${0:x}) = x {} +// if let Some(${0:x1}) = x {} // } // ``` pub(crate) fn replace_is_method_with_if_let_method( @@ -40,10 +40,13 @@ pub(crate) fn replace_is_method_with_if_let_method( "is_some" | "is_ok" => { let receiver = call_expr.receiver()?; + let mut name_generator = suggest_name::NameGenerator::new_from_scope_locals( + ctx.sema.scope(if_expr.syntax()), + ); let var_name = if let ast::Expr::PathExpr(path_expr) = receiver.clone() { - path_expr.path()?.to_string() + name_generator.suggest_name(&path_expr.path()?.to_string()) } else { - suggest_name::for_variable(&receiver, &ctx.sema) + name_generator.for_variable(&receiver, &ctx.sema) }; let (assist_id, message, text) = if name_ref.text() == "is_some" { @@ -98,7 +101,7 @@ fn main() { r#" fn main() { let x = Some(1); - if let Some(${0:x}) = x {} + if let Some(${0:x1}) = x {} } "#, ); @@ -150,7 +153,7 @@ fn main() { r#" fn main() { let x = Ok(1); - if let Ok(${0:x}) = x {} + if let Ok(${0:x1}) = x {} } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index 1101c20f1b78..f026b3230dd6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -159,7 +159,7 @@ fn path_eq_no_generics(lhs: ast::Path, rhs: ast::Path) -> bool { && lhs .name_ref() .zip(rhs.name_ref()) - .map_or(false, |(lhs, rhs)| lhs.text() == rhs.text()) => {} + .is_some_and(|(lhs, rhs)| lhs.text() == rhs.text()) => {} _ => return false, } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs index 648bf358b4bb..c6cffb5434a5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs @@ -65,7 +65,7 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let mut pipe_index = pipe_token.index(); if pipe_token .prev_sibling_or_token() - .map_or(false, |it| it.kind() == SyntaxKind::WHITESPACE) + .is_some_and(|it| it.kind() == SyntaxKind::WHITESPACE) { pipe_index -= 1; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs index 52df30d9623f..38ca572fa660 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_use.rs @@ -66,7 +66,7 @@ fn resolve_full_path(tree: &ast::UseTree) -> Option { .filter_map(|t| t.path()); let final_path = paths.reduce(|prev, next| make::path_concat(next, prev))?; - if final_path.segment().map_or(false, |it| it.self_token().is_some()) { + if final_path.segment().is_some_and(|it| it.self_token().is_some()) { final_path.qualifier() } else { Some(final_path) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 78fdfba6a07d..54e42f126bc5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -2885,7 +2885,7 @@ fn main() { r#####" fn main() { let x = Some(1); - if let Some(${0:x}) = x {} + if let Some(${0:x1}) = x {} } "#####, ) 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 3c26b0435977..e20c4ef09e8c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -316,43 +316,73 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool { pat_head == var_head } -pub(crate) fn does_nested_pattern(pat: &ast::Pat) -> bool { - let depth = calc_depth(pat, 0); - - if 1 < depth { - return true; - } - false +pub(crate) fn does_pat_variant_nested_or_literal(ctx: &AssistContext<'_>, pat: &ast::Pat) -> bool { + check_pat_variant_nested_or_literal_with_depth(ctx, pat, 0) } -fn calc_depth(pat: &ast::Pat, depth: usize) -> usize { - match pat { - ast::Pat::IdentPat(_) - | ast::Pat::BoxPat(_) - | ast::Pat::RestPat(_) - | ast::Pat::LiteralPat(_) - | ast::Pat::MacroPat(_) - | ast::Pat::OrPat(_) - | ast::Pat::ParenPat(_) - | ast::Pat::PathPat(_) - | ast::Pat::WildcardPat(_) - | ast::Pat::RangePat(_) - | ast::Pat::RecordPat(_) - | ast::Pat::RefPat(_) - | ast::Pat::SlicePat(_) - | ast::Pat::TuplePat(_) - | ast::Pat::ConstBlockPat(_) => depth, +fn check_pat_variant_from_enum(ctx: &AssistContext<'_>, pat: &ast::Pat) -> bool { + ctx.sema.type_of_pat(pat).is_none_or(|ty: hir::TypeInfo| { + ty.adjusted().as_adt().is_some_and(|adt| matches!(adt, hir::Adt::Enum(_))) + }) +} - // FIXME: Other patterns may also be nested. Currently it simply supports only `TupleStructPat` - ast::Pat::TupleStructPat(pat) => { - let mut max_depth = depth; - for p in pat.fields() { - let d = calc_depth(&p, depth + 1); - if d > max_depth { - max_depth = d - } - } - max_depth +fn check_pat_variant_nested_or_literal_with_depth( + ctx: &AssistContext<'_>, + pat: &ast::Pat, + depth_after_refutable: usize, +) -> bool { + if depth_after_refutable > 1 { + return true; + } + + match pat { + ast::Pat::RestPat(_) | ast::Pat::WildcardPat(_) | ast::Pat::RefPat(_) => false, + + ast::Pat::LiteralPat(_) + | ast::Pat::RangePat(_) + | ast::Pat::MacroPat(_) + | ast::Pat::PathPat(_) + | ast::Pat::BoxPat(_) + | ast::Pat::ConstBlockPat(_) => true, + + ast::Pat::IdentPat(ident_pat) => ident_pat.pat().is_some_and(|pat| { + check_pat_variant_nested_or_literal_with_depth(ctx, &pat, depth_after_refutable) + }), + ast::Pat::ParenPat(paren_pat) => paren_pat.pat().is_none_or(|pat| { + check_pat_variant_nested_or_literal_with_depth(ctx, &pat, depth_after_refutable) + }), + ast::Pat::TuplePat(tuple_pat) => tuple_pat.fields().any(|pat| { + check_pat_variant_nested_or_literal_with_depth(ctx, &pat, depth_after_refutable) + }), + ast::Pat::RecordPat(record_pat) => { + let adjusted_next_depth = + depth_after_refutable + if check_pat_variant_from_enum(ctx, pat) { 1 } else { 0 }; + record_pat.record_pat_field_list().is_none_or(|pat| { + pat.fields().any(|pat| { + pat.pat().is_none_or(|pat| { + check_pat_variant_nested_or_literal_with_depth( + ctx, + &pat, + adjusted_next_depth, + ) + }) + }) + }) + } + ast::Pat::OrPat(or_pat) => or_pat.pats().any(|pat| { + check_pat_variant_nested_or_literal_with_depth(ctx, &pat, depth_after_refutable) + }), + ast::Pat::TupleStructPat(tuple_struct_pat) => { + let adjusted_next_depth = + depth_after_refutable + if check_pat_variant_from_enum(ctx, pat) { 1 } else { 0 }; + tuple_struct_pat.fields().any(|pat| { + check_pat_variant_nested_or_literal_with_depth(ctx, &pat, adjusted_next_depth) + }) + } + ast::Pat::SlicePat(slice_pat) => { + let mut pats = slice_pat.pats(); + pats.next() + .is_none_or(|pat| !matches!(pat, ast::Pat::RestPat(_)) || pats.next().is_some()) } } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs index c5a91e478bf8..75caf6d49f75 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -32,7 +32,7 @@ pub(crate) fn gen_trait_fn_body( /// Generate a `Clone` impl based on the fields and members of the target type. fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - stdx::always!(func.name().map_or(false, |name| name.text() == "clone")); + stdx::always!(func.name().is_some_and(|name| name.text() == "clone")); fn gen_clone_call(target: ast::Expr) -> ast::Expr { let method = make::name_ref("clone"); make::expr_method_call(target, method, make::arg_list(None)) @@ -344,7 +344,7 @@ fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// Generate a `Hash` impl based on the fields and members of the target type. fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - stdx::always!(func.name().map_or(false, |name| name.text() == "hash")); + stdx::always!(func.name().is_some_and(|name| name.text() == "hash")); fn gen_hash_call(target: ast::Expr) -> ast::Stmt { let method = make::name_ref("hash"); let arg = make::expr_path(make::ext::ident_path("state")); @@ -400,7 +400,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// Generate a `PartialEq` impl based on the fields and members of the target type. fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) -> Option<()> { - stdx::always!(func.name().map_or(false, |name| name.text() == "eq")); + stdx::always!(func.name().is_some_and(|name| name.text() == "eq")); fn gen_eq_chain(expr: Option, cmp: ast::Expr) -> Option { match expr { Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)), @@ -592,7 +592,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) - } fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option) -> Option<()> { - stdx::always!(func.name().map_or(false, |name| name.text() == "partial_cmp")); + stdx::always!(func.name().is_some_and(|name| name.text() == "partial_cmp")); fn gen_partial_eq_match(match_target: ast::Expr) -> Option { let mut arms = vec![]; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 229ce7723b57..26074672ba9c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -1,6 +1,8 @@ //! Completes references after dot (fields and method calls). -use hir::{sym, Name}; +use std::ops::ControlFlow; + +use hir::{sym, HasContainer, ItemContainer, MethodCandidateCallback, Name}; use ide_db::FxHashSet; use syntax::SmolStr; @@ -158,21 +160,55 @@ fn complete_fields( fn complete_methods( ctx: &CompletionContext<'_>, receiver: &hir::Type, - mut f: impl FnMut(hir::Function), + f: impl FnMut(hir::Function), ) { - let mut seen_methods = FxHashSet::default(); - receiver.iterate_method_candidates_with_traits( + struct Callback<'a, F> { + ctx: &'a CompletionContext<'a>, + f: F, + seen_methods: FxHashSet, + } + + impl MethodCandidateCallback for Callback<'_, F> + where + F: FnMut(hir::Function), + { + // We don't want to exclude inherent trait methods - that is, methods of traits available from + // `where` clauses or `dyn Trait`. + fn on_inherent_method(&mut self, func: hir::Function) -> ControlFlow<()> { + if func.self_param(self.ctx.db).is_some() + && self.seen_methods.insert(func.name(self.ctx.db)) + { + (self.f)(func); + } + ControlFlow::Continue(()) + } + + fn on_trait_method(&mut self, func: hir::Function) -> ControlFlow<()> { + // This needs to come before the `seen_methods` test, so that if we see the same method twice, + // once as inherent and once not, we will include it. + if let ItemContainer::Trait(trait_) = func.container(self.ctx.db) { + if self.ctx.exclude_traits.contains(&trait_) { + return ControlFlow::Continue(()); + } + } + + if func.self_param(self.ctx.db).is_some() + && self.seen_methods.insert(func.name(self.ctx.db)) + { + (self.f)(func); + } + + ControlFlow::Continue(()) + } + } + + receiver.iterate_method_candidates_split_inherent( ctx.db, &ctx.scope, &ctx.traits_in_scope(), Some(ctx.module), None, - |func| { - if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) { - f(func); - } - None::<()> - }, + Callback { ctx, f, seen_methods: FxHashSet::default() }, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs index c9013d1d17d4..0b6790d42a61 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs @@ -43,7 +43,7 @@ pub(crate) fn complete_cargo_env_vars( .sema .hir_file_for(&expanded.syntax().parent()?) .macro_file() - .map_or(false, |it| it.is_env_or_option_env(ctx.sema.db)); + .is_some_and(|it| it.is_env_or_option_env(ctx.sema.db)); if !is_in_env_expansion { let call = macro_call_for_string_token(expanded)?; let makro = ctx.sema.resolve_macro_call(&call)?; 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 ff2c8da42130..c2e5eefe1017 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 @@ -1,6 +1,9 @@ //! Completion of names from the current scope in expression position. -use hir::{sym, Name, ScopeDef}; +use std::ops::ControlFlow; + +use hir::{sym, Name, PathCandidateCallback, ScopeDef}; +use ide_db::FxHashSet; use syntax::ast; use crate::{ @@ -9,6 +12,38 @@ use crate::{ CompletionContext, Completions, }; +struct PathCallback<'a, F> { + ctx: &'a CompletionContext<'a>, + acc: &'a mut Completions, + add_assoc_item: F, + seen: FxHashSet, +} + +impl PathCandidateCallback for PathCallback<'_, F> +where + F: FnMut(&mut Completions, hir::AssocItem), +{ + fn on_inherent_item(&mut self, item: hir::AssocItem) -> ControlFlow<()> { + if self.seen.insert(item) { + (self.add_assoc_item)(self.acc, item); + } + ControlFlow::Continue(()) + } + + fn on_trait_item(&mut self, item: hir::AssocItem) -> ControlFlow<()> { + // The excluded check needs to come before the `seen` test, so that if we see the same method twice, + // once as inherent and once not, we will include it. + if item + .container_trait(self.ctx.db) + .is_none_or(|trait_| !self.ctx.exclude_traits.contains(&trait_)) + && self.seen.insert(item) + { + (self.add_assoc_item)(self.acc, item); + } + ControlFlow::Continue(()) + } +} + pub(crate) fn complete_expr_path( acc: &mut Completions, ctx: &CompletionContext<'_>, @@ -50,12 +85,18 @@ pub(crate) fn complete_expr_path( }; match qualified { + // We exclude associated types/consts of excluded traits here together with methods, + // even though we don't exclude them when completing in type position, because it's easier. Qualified::TypeAnchor { ty: None, trait_: None } => ctx .traits_in_scope() .iter() - .flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db)) + .copied() + .map(hir::Trait::from) + .filter(|it| !ctx.exclude_traits.contains(it)) + .flat_map(|it| it.items(ctx.sema.db)) .for_each(|item| add_assoc_item(acc, item)), Qualified::TypeAnchor { trait_: Some(trait_), .. } => { + // Don't filter excluded traits here, user requested this specific trait. trait_.items(ctx.sema.db).into_iter().for_each(|item| add_assoc_item(acc, item)) } Qualified::TypeAnchor { ty: Some(ty), trait_: None } => { @@ -64,9 +105,14 @@ pub(crate) fn complete_expr_path( acc.add_enum_variants(ctx, path_ctx, e); } - ctx.iterate_path_candidates(ty, |item| { - add_assoc_item(acc, item); - }); + ty.iterate_path_candidates_split_inherent( + ctx.db, + &ctx.scope, + &ctx.traits_in_scope(), + Some(ctx.module), + None, + PathCallback { ctx, acc, add_assoc_item, seen: FxHashSet::default() }, + ); // Iterate assoc types separately ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { @@ -121,9 +167,14 @@ pub(crate) fn complete_expr_path( // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. // (where AssocType is defined on a trait, not an inherent impl) - ctx.iterate_path_candidates(&ty, |item| { - add_assoc_item(acc, item); - }); + ty.iterate_path_candidates_split_inherent( + ctx.db, + &ctx.scope, + &ctx.traits_in_scope(), + Some(ctx.module), + None, + PathCallback { ctx, acc, add_assoc_item, seen: FxHashSet::default() }, + ); // Iterate assoc types separately ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { @@ -134,6 +185,7 @@ pub(crate) fn complete_expr_path( }); } hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => { + // Don't filter excluded traits here, user requested this specific trait. // Handles `Trait::assoc` as well as `::assoc`. for item in t.items(ctx.db) { add_assoc_item(acc, item); @@ -151,9 +203,14 @@ pub(crate) fn complete_expr_path( acc.add_enum_variants(ctx, path_ctx, e); } - ctx.iterate_path_candidates(&ty, |item| { - add_assoc_item(acc, item); - }); + ty.iterate_path_candidates_split_inherent( + ctx.db, + &ctx.scope, + &ctx.traits_in_scope(), + Some(ctx.module), + None, + PathCallback { ctx, acc, add_assoc_item, seen: FxHashSet::default() }, + ); } _ => (), } @@ -236,9 +293,17 @@ pub(crate) fn complete_expr_path( [..] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases), } } + // synthetic names currently leak out as we lack synthetic hygiene, so filter them + // out here + ScopeDef::Local(_) => { + if !name.as_str().starts_with('<') { + acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) + } + } _ if scope_def_applicable(def) => { acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) } + _ => (), }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs index 847fa4cf889d..bcfda928c4da 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs @@ -46,7 +46,7 @@ pub(crate) fn complete_extern_abi( ctx: &CompletionContext<'_>, expanded: &ast::String, ) -> Option<()> { - if !expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) { + if !expanded.syntax().parent().is_some_and(|it| ast::Abi::can_cast(it.kind())) { return None; } let abi_str = expanded; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 2a6b310d3a21..3b2b2fd706e1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -8,6 +8,7 @@ use itertools::Itertools; use syntax::{ast, AstNode, SyntaxNode, ToSmolStr, T}; use crate::{ + config::AutoImportExclusionType, context::{ CompletionContext, DotAccess, PathCompletionCtx, PathKind, PatternContext, Qualified, TypeLocation, @@ -267,6 +268,19 @@ fn import_on_the_fly( && !ctx.is_item_hidden(original_item) && ctx.check_stability(original_item.attrs(ctx.db).as_deref()) }) + .filter(|import| { + let def = import.item_to_import.into_module_def(); + if let Some(&kind) = ctx.exclude_flyimport.get(&def) { + if kind == AutoImportExclusionType::Always { + return false; + } + let method_imported = import.item_to_import != import.original_item; + if method_imported { + return false; + } + } + true + }) .sorted_by(|a, b| { let key = |import_path| { ( @@ -352,6 +366,24 @@ fn import_on_the_fly_method( !ctx.is_item_hidden(&import.item_to_import) && !ctx.is_item_hidden(&import.original_item) }) + .filter(|import| { + let def = import.item_to_import.into_module_def(); + if let Some(&kind) = ctx.exclude_flyimport.get(&def) { + if kind == AutoImportExclusionType::Always { + return false; + } + let method_imported = import.item_to_import != import.original_item; + if method_imported { + return false; + } + } + + if let ModuleDef::Trait(_) = import.item_to_import.into_module_def() { + !ctx.exclude_flyimport.contains_key(&def) + } else { + true + } + }) .sorted_by(|a, b| { let key = |import_path| { ( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs index ee3b817ee8cf..e86eaad4d0f2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs @@ -167,7 +167,7 @@ fn should_add_self_completions( return false; } match param_list.params().next() { - Some(first) => first.pat().map_or(false, |pat| pat.syntax().text_range().contains(cursor)), + Some(first) => first.pat().is_some_and(|pat| pat.syntax().text_range().contains(cursor)), None => true, } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs index 117a5e3d9359..d0c4c24d060f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs @@ -93,7 +93,7 @@ pub(crate) fn add_default_update( let default_trait = ctx.famous_defs().core_default_Default(); let impls_default_trait = default_trait .zip(ty.as_ref()) - .map_or(false, |(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[])); + .is_some_and(|(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[])); if impls_default_trait { // FIXME: This should make use of scope_def like completions so we get all the other goodies // that is we should handle this like actually completing the default function diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs index 45704549e609..9d62622add20 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs @@ -101,7 +101,7 @@ pub(crate) fn complete_use_path( ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => { // exclude prelude enum let is_builtin = - res.krate(ctx.db).map_or(false, |krate| krate.is_builtin(ctx.db)); + res.krate(ctx.db).is_some_and(|krate| krate.is_builtin(ctx.db)); if !is_builtin { let item = CompletionItem::new( 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 1d05419c96de..8b1ce11e8a45 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -10,7 +10,7 @@ use ide_db::{imports::insert_use::InsertUseConfig, SnippetCap}; use crate::{snippet::Snippet, CompletionFieldsToResolve}; #[derive(Clone, Debug, PartialEq, Eq)] -pub struct CompletionConfig { +pub struct CompletionConfig<'a> { pub enable_postfix_completions: bool, pub enable_imports_on_the_fly: bool, pub enable_self_on_the_fly: bool, @@ -28,6 +28,14 @@ pub struct CompletionConfig { pub snippets: Vec, pub limit: Option, pub fields_to_resolve: CompletionFieldsToResolve, + pub exclude_flyimport: Vec<(String, AutoImportExclusionType)>, + pub exclude_traits: &'a [String], +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum AutoImportExclusionType { + Always, + Methods, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -36,7 +44,7 @@ pub enum CallableSnippets { AddParentheses, } -impl CompletionConfig { +impl CompletionConfig<'_> { pub fn postfix_snippets(&self) -> impl Iterator { self.snippets .iter() 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 f8d403122d13..3705e2c73d64 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -7,8 +7,8 @@ mod tests; use std::{iter, ops::ControlFlow}; use hir::{ - HasAttrs, Local, ModuleSource, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, - Symbol, Type, TypeInfo, + HasAttrs, Local, ModPath, ModuleDef, ModuleSource, Name, PathResolution, ScopeDef, Semantics, + SemanticsScope, Symbol, Type, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, famous_defs::FamousDefs, helpers::is_editable_crate, FilePosition, @@ -22,6 +22,7 @@ use syntax::{ }; use crate::{ + config::AutoImportExclusionType, context::analysis::{expand_and_analyze, AnalysisResult}, CompletionConfig, }; @@ -429,7 +430,7 @@ pub(crate) struct CompletionContext<'a> { pub(crate) sema: Semantics<'a, RootDatabase>, pub(crate) scope: SemanticsScope<'a>, pub(crate) db: &'a RootDatabase, - pub(crate) config: &'a CompletionConfig, + pub(crate) config: &'a CompletionConfig<'a>, pub(crate) position: FilePosition, /// The token before the cursor, in the original file. @@ -462,6 +463,17 @@ pub(crate) struct CompletionContext<'a> { /// Here depth will be 2 pub(crate) depth_from_crate_root: usize, + /// Traits whose methods will be excluded from flyimport. Flyimport should not suggest + /// importing those traits. + /// + /// Note the trait *themselves* are not excluded, only their methods are. + pub(crate) exclude_flyimport: FxHashMap, + /// Traits whose methods should always be excluded, even when in scope (compare `exclude_flyimport_traits`). + /// They will *not* be excluded, however, if they are available as a generic bound. + /// + /// Note the trait *themselves* are not excluded, only their methods are. + pub(crate) exclude_traits: FxHashSet, + /// Whether and how to complete semicolon for unit-returning functions. pub(crate) complete_semicolon: CompleteSemicolon, } @@ -670,7 +682,7 @@ impl<'a> CompletionContext<'a> { pub(crate) fn new( db: &'a RootDatabase, position @ FilePosition { file_id, offset }: FilePosition, - config: &'a CompletionConfig, + config: &'a CompletionConfig<'a>, ) -> Option<(CompletionContext<'a>, CompletionAnalysis)> { let _p = tracing::info_span!("CompletionContext::new").entered(); let sema = Semantics::new(db); @@ -742,17 +754,53 @@ impl<'a> CompletionContext<'a> { let mut locals = FxHashMap::default(); scope.process_all_names(&mut |name, scope| { if let ScopeDef::Local(local) = scope { + // synthetic names currently leak out as we lack synthetic hygiene, so filter them + // out here + if name.as_str().starts_with('<') { + return; + } locals.insert(name, local); } }); let depth_from_crate_root = iter::successors(Some(module), |m| m.parent(db)) - // `BlockExpr` modules are not count as module depth + // `BlockExpr` modules do not count towards module depth .filter(|m| !matches!(m.definition_source(db).value, ModuleSource::BlockExpr(_))) .count() // exclude `m` itself .saturating_sub(1); + let exclude_traits: FxHashSet<_> = config + .exclude_traits + .iter() + .filter_map(|path| { + scope + .resolve_mod_path(&ModPath::from_segments( + hir::PathKind::Plain, + path.split("::").map(Symbol::intern).map(Name::new_symbol_root), + )) + .find_map(|it| match it { + hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t), + _ => None, + }) + }) + .collect(); + + let mut exclude_flyimport: FxHashMap<_, _> = config + .exclude_flyimport + .iter() + .flat_map(|(path, kind)| { + scope + .resolve_mod_path(&ModPath::from_segments( + hir::PathKind::Plain, + path.split("::").map(Symbol::intern).map(Name::new_symbol_root), + )) + .map(|it| (it.into_module_def(), *kind)) + }) + .collect(); + exclude_flyimport + .extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always))); + let complete_semicolon = if config.add_semicolon_to_unit { let inside_closure_ret = token.parent_ancestors().try_for_each(|ancestor| { match_ast! { @@ -817,6 +865,8 @@ impl<'a> CompletionContext<'a> { qualifier_ctx, locals, depth_from_crate_root, + exclude_flyimport, + exclude_traits, complete_semicolon, }; Some((ctx, analysis)) 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 1c4cbb25b1fd..acce62a041cf 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 @@ -417,6 +417,15 @@ fn analyze( derive_ctx, } = expansion_result; + if original_token.kind() != self_token.kind() + // FIXME: This check can be removed once we use speculative database forking for completions + && !(original_token.kind().is_punct() || original_token.kind().is_trivia()) + && !(SyntaxKind::is_any_identifier(original_token.kind()) + && SyntaxKind::is_any_identifier(self_token.kind())) + { + return None; + } + // Overwrite the path kind for derives if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx { if let Some(ast::NameLike::NameRef(name_ref)) = @@ -456,7 +465,7 @@ fn analyze( && p.ancestors().any(|it| it.kind() == SyntaxKind::META) { let colon_prefix = previous_non_trivia_token(self_token.clone()) - .map_or(false, |it| T![:] == it.kind()); + .is_some_and(|it| T![:] == it.kind()); CompletionAnalysis::UnexpandedAttrTT { fake_attribute_under_caret: fake_ident_token @@ -643,7 +652,7 @@ fn expected_type_and_name( // match foo { $0 } // match foo { ..., pat => $0 } ast::MatchExpr(it) => { - let on_arrow = previous_non_trivia_token(token.clone()).map_or(false, |it| T![=>] == it.kind()); + let on_arrow = previous_non_trivia_token(token.clone()).is_some_and(|it| T![=>] == it.kind()); let ty = if on_arrow { // match foo { ..., pat => $0 } @@ -780,7 +789,7 @@ fn classify_name_ref( if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) { let dot_prefix = previous_non_trivia_token(name_ref.syntax().clone()) - .map_or(false, |it| T![.] == it.kind()); + .is_some_and(|it| T![.] == it.kind()); return find_node_in_file_compensated( sema, @@ -814,7 +823,7 @@ fn classify_name_ref( let receiver_is_ambiguous_float_literal = match &receiver { Some(ast::Expr::Literal(l)) => matches! { l.kind(), - ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().map_or(false, |it| it.text().ends_with('.')) + ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().is_some_and(|it| it.text().ends_with('.')) }, _ => false, }; @@ -846,7 +855,7 @@ fn classify_name_ref( let receiver = find_opt_node_in_file(original_file, method.receiver()); let kind = NameRefKind::DotAccess(DotAccess { receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)), - kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) }, + kind: DotAccessKind::Method { has_parens: method.arg_list().is_some_and(|it| it.l_paren_token().is_some()) }, receiver, ctx: DotAccessExprCtx { in_block_expr: is_in_block(method.syntax()), in_breakable: is_in_breakable(method.syntax()) } }); @@ -1193,13 +1202,13 @@ fn classify_name_ref( let incomplete_let = it .parent() .and_then(ast::LetStmt::cast) - .map_or(false, |it| it.semicolon_token().is_none()); + .is_some_and(|it| it.semicolon_token().is_none()); let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); let in_match_guard = match it.parent().and_then(ast::MatchArm::cast) { Some(arm) => arm .fat_arrow_token() - .map_or(true, |arrow| it.text_range().start() < arrow.text_range().start()), + .is_none_or(|arrow| it.text_range().start() < arrow.text_range().start()), None => false, }; @@ -1329,7 +1338,7 @@ fn classify_name_ref( } } - path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); + path_ctx.has_call_parens = it.syntax().parent().is_some_and(|it| ast::CallExpr::can_cast(it.kind())); make_path_kind_expr(it.into()) }, @@ -1358,7 +1367,7 @@ fn classify_name_ref( match parent { ast::PathType(it) => make_path_kind_type(it.into()), ast::PathExpr(it) => { - path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); + path_ctx.has_call_parens = it.syntax().parent().is_some_and(|it| ast::CallExpr::can_cast(it.kind())); make_path_kind_expr(it.into()) }, @@ -1612,8 +1621,7 @@ fn pattern_context_for( &pat, ast::Pat::IdentPat(it) if it.syntax() - .parent() - .map_or(false, |node| { + .parent().is_some_and(|node| { let kind = node.kind(); ast::LetStmt::can_cast(kind) || ast::Param::can_cast(kind) }) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 9608eed99d8f..b91f915619d7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -631,7 +631,7 @@ impl Builder { self.set_documentation(Some(docs)) } pub(crate) fn set_documentation(&mut self, docs: Option) -> &mut Builder { - self.documentation = docs.map(Into::into); + self.documentation = docs; self } pub(crate) fn set_deprecated(&mut self, deprecated: bool) -> &mut Builder { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 14f42b40055e..ca6c9ad9f083 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -31,7 +31,7 @@ use crate::{ }; pub use crate::{ - config::{CallableSnippets, CompletionConfig}, + config::{AutoImportExclusionType, CallableSnippets, CompletionConfig}, item::{ CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch, CompletionRelevanceReturnType, CompletionRelevanceTypeMatch, @@ -184,7 +184,7 @@ impl CompletionFieldsToResolve { /// analysis. pub fn completions( db: &RootDatabase, - config: &CompletionConfig, + config: &CompletionConfig<'_>, position: FilePosition, trigger_character: Option, ) -> Option> { @@ -269,7 +269,7 @@ pub fn completions( /// This is used for import insertion done via completions like flyimport and custom user snippets. pub fn resolve_completion_edits( db: &RootDatabase, - config: &CompletionConfig, + config: &CompletionConfig<'_>, FilePosition { file_id, offset }: FilePosition, imports: impl IntoIterator, ) -> Option> { 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 baa30b286308..c239ca512da3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -86,11 +86,7 @@ impl<'a> RenderContext<'a> { fn is_immediately_after_macro_bang(&self) -> bool { self.completion.token.kind() == SyntaxKind::BANG - && self - .completion - .token - .parent() - .map_or(false, |it| it.kind() == SyntaxKind::MACRO_CALL) + && self.completion.token.parent().is_some_and(|it| it.kind() == SyntaxKind::MACRO_CALL) } fn is_deprecated(&self, def: impl HasAttrs) -> bool { @@ -636,7 +632,7 @@ fn compute_type_match( } fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool { - ctx.expected_name.as_ref().map_or(false, |name| name.text() == completion_name) + ctx.expected_name.as_ref().is_some_and(|name| name.text() == completion_name) } fn compute_ref_match( @@ -778,7 +774,7 @@ mod tests { relevance.postfix_match == Some(CompletionRelevancePostfixMatch::Exact), "snippet", ), - (relevance.trait_.map_or(false, |it| it.is_op_method), "op_method"), + (relevance.trait_.is_some_and(|it| it.is_op_method), "op_method"), (relevance.requires_import, "requires_import"), ] .into_iter() diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 5265aa8515b6..04bb178c658f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -100,9 +100,9 @@ // } // ---- +use hir::{ModPath, Name, Symbol}; use ide_db::imports::import_assets::LocatedImport; use itertools::Itertools; -use syntax::{ast, AstNode, GreenNode, SyntaxNode}; use crate::context::CompletionContext; @@ -123,10 +123,7 @@ pub struct Snippet { pub scope: SnippetScope, pub description: Option>, snippet: String, - // These are `ast::Path`'s but due to SyntaxNodes not being Send we store these - // and reconstruct them on demand instead. This is cheaper than reparsing them - // from strings - requires: Box<[GreenNode]>, + requires: Box<[ModPath]>, } impl Snippet { @@ -143,7 +140,6 @@ impl Snippet { } let (requires, snippet, description) = validate_snippet(snippet, description, requires)?; Some(Snippet { - // Box::into doesn't work as that has a Copy bound 😒 postfix_triggers: postfix_triggers.iter().map(String::as_str).map(Into::into).collect(), prefix_triggers: prefix_triggers.iter().map(String::as_str).map(Into::into).collect(), scope, @@ -167,15 +163,11 @@ impl Snippet { } } -fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option> { +fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option> { let import_cfg = ctx.config.import_path_config(); - let resolve = |import: &GreenNode| { - let path = ast::Path::cast(SyntaxNode::new_root(import.clone()))?; - let item = match ctx.scope.speculative_resolve(&path)? { - hir::PathResolution::Def(def) => def.into(), - _ => return None, - }; + let resolve = |import| { + let item = ctx.scope.resolve_mod_path(import).next()?; let path = ctx.module.find_use_path( ctx.db, item, @@ -198,19 +190,14 @@ fn validate_snippet( snippet: &[String], description: &str, requires: &[String], -) -> Option<(Box<[GreenNode]>, String, Option>)> { +) -> Option<(Box<[ModPath]>, String, Option>)> { let mut imports = Vec::with_capacity(requires.len()); for path in requires.iter() { - let use_path = - ast::SourceFile::parse(&format!("use {path};"), syntax::Edition::CURRENT_FIXME) - .syntax_node() - .descendants() - .find_map(ast::Path::cast)?; - if use_path.syntax().text() != path.as_str() { - return None; - } - let green = use_path.syntax().green().into_owned(); - imports.push(green); + let use_path = ModPath::from_segments( + hir::PathKind::Plain, + path.split("::").map(Symbol::intern).map(Name::new_symbol_root), + ); + imports.push(use_path); } let snippet = snippet.iter().join("\n"); let description = (!description.is_empty()) 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 e01097a9105b..1815f3405321 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -61,7 +61,7 @@ fn function() {} union Union { field: i32 } "#; -pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { +pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig { enable_postfix_completions: true, enable_imports_on_the_fly: true, enable_self_on_the_fly: true, @@ -85,6 +85,8 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { snippets: Vec::new(), limit: None, fields_to_resolve: CompletionFieldsToResolve::empty(), + exclude_flyimport: vec![], + exclude_traits: &[], }; pub(crate) fn completion_list(ra_fixture: &str) -> String { @@ -109,7 +111,7 @@ pub(crate) fn completion_list_with_trigger_character( } fn completion_list_with_config_raw( - config: CompletionConfig, + config: CompletionConfig<'_>, ra_fixture: &str, include_keywords: bool, trigger_character: Option, @@ -132,7 +134,7 @@ fn completion_list_with_config_raw( } fn completion_list_with_config( - config: CompletionConfig, + config: CompletionConfig<'_>, ra_fixture: &str, include_keywords: bool, trigger_character: Option, @@ -161,7 +163,7 @@ pub(crate) fn do_completion(code: &str, kind: CompletionItemKind) -> Vec, code: &str, kind: CompletionItemKind, ) -> Vec { @@ -220,7 +222,7 @@ pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: #[track_caller] pub(crate) fn check_edit_with_config( - config: CompletionConfig, + config: CompletionConfig<'_>, what: &str, ra_fixture_before: &str, ra_fixture_after: &str, @@ -257,7 +259,7 @@ fn check_empty(ra_fixture: &str, expect: Expect) { } pub(crate) fn get_all_items( - config: CompletionConfig, + config: CompletionConfig<'_>, code: &str, trigger_character: Option, ) -> Vec { 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 acafa6518f69..ebf358205706 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 @@ -713,6 +713,28 @@ struct Foo; ); } +#[test] +fn issue_17479() { + check( + r#" +//- proc_macros: issue_17479 +fn main() { + proc_macros::issue_17479!("te$0"); +} +"#, + expect![""], + ); + check( + r#" +//- proc_macros: issue_17479 +fn main() { + proc_macros::issue_17479!("$0"); +} +"#, + expect![""], + ) +} + mod cfg { use super::*; 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 ea1b7ad78719..304661486864 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 @@ -1,13 +1,30 @@ //! Completion tests for expressions. use expect_test::{expect, Expect}; -use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE}; +use crate::{ + config::AutoImportExclusionType, + tests::{ + check_edit, check_empty, completion_list, completion_list_with_config, BASE_ITEMS_FIXTURE, + TEST_CONFIG, + }, + CompletionConfig, +}; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}")); expect.assert_eq(&actual) } +fn check_with_config(config: CompletionConfig<'_>, ra_fixture: &str, expect: Expect) { + let actual = completion_list_with_config( + config, + &format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"), + true, + None, + ); + expect.assert_eq(&actual) +} + #[test] fn complete_literal_struct_with_a_private_field() { // `FooDesc.bar` is private, the completion should not be triggered. @@ -1390,3 +1407,453 @@ fn main() { "#]], ); } + +#[test] +fn excluded_trait_method_is_excluded() { + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl ExcludedTrait for T {} + +struct Foo; +impl Foo { + fn inherent(&self) {} +} + +fn foo() { + Foo.$0 +} + "#, + expect![[r#" + me bar() (as ExcludedTrait) fn(&self) + me baz() (as ExcludedTrait) fn(&self) + me foo() (as ExcludedTrait) fn(&self) + me inherent() fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); +} + +#[test] +fn excluded_trait_not_excluded_when_inherent() { + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl ExcludedTrait for T {} + +fn foo(v: &dyn ExcludedTrait) { + v.$0 +} + "#, + expect![[r#" + me bar() (as ExcludedTrait) fn(&self) + me baz() (as ExcludedTrait) fn(&self) + me foo() (as ExcludedTrait) fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl ExcludedTrait for T {} + +fn foo(v: impl ExcludedTrait) { + v.$0 +} + "#, + expect![[r#" + me bar() (as ExcludedTrait) fn(&self) + me baz() (as ExcludedTrait) fn(&self) + me foo() (as ExcludedTrait) fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl ExcludedTrait for T {} + +fn foo(v: T) { + v.$0 +} + "#, + expect![[r#" + me bar() (as ExcludedTrait) fn(&self) + me baz() (as ExcludedTrait) fn(&self) + me foo() (as ExcludedTrait) fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); +} + +#[test] +fn excluded_trait_method_is_excluded_from_flyimport() { + check_with_config( + CompletionConfig { + exclude_traits: &["test::module2::ExcludedTrait".to_owned()], + ..TEST_CONFIG + }, + r#" +mod module2 { + pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} + } + + impl ExcludedTrait for T {} +} + +struct Foo; +impl Foo { + fn inherent(&self) {} +} + +fn foo() { + Foo.$0 +} + "#, + expect![[r#" + me bar() (use module2::ExcludedTrait) fn(&self) + me baz() (use module2::ExcludedTrait) fn(&self) + me foo() (use module2::ExcludedTrait) fn(&self) + me inherent() fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); +} + +#[test] +fn flyimport_excluded_trait_method_is_excluded_from_flyimport() { + check_with_config( + CompletionConfig { + exclude_flyimport: vec![( + "test::module2::ExcludedTrait".to_owned(), + AutoImportExclusionType::Methods, + )], + ..TEST_CONFIG + }, + r#" +mod module2 { + pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} + } + + impl ExcludedTrait for T {} +} + +struct Foo; +impl Foo { + fn inherent(&self) {} +} + +fn foo() { + Foo.$0 +} + "#, + expect![[r#" + me bar() (use module2::ExcludedTrait) fn(&self) + me baz() (use module2::ExcludedTrait) fn(&self) + me foo() (use module2::ExcludedTrait) fn(&self) + me inherent() fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); +} + +#[test] +fn excluded_trait_method_is_excluded_from_path_completion() { + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl ExcludedTrait for T {} + +struct Foo; +impl Foo { + fn inherent(&self) {} +} + +fn foo() { + Foo::$0 +} + "#, + expect![[r#" + me bar(…) (as ExcludedTrait) fn(&self) + me baz(…) (as ExcludedTrait) fn(&self) + me foo(…) (as ExcludedTrait) fn(&self) + me inherent(…) fn(&self) + "#]], + ); +} + +#[test] +fn excluded_trait_method_is_not_excluded_when_trait_is_specified() { + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl ExcludedTrait for T {} + +struct Foo; +impl Foo { + fn inherent(&self) {} +} + +fn foo() { + ExcludedTrait::$0 +} + "#, + expect![[r#" + me bar(…) (as ExcludedTrait) fn(&self) + me baz(…) (as ExcludedTrait) fn(&self) + me foo(…) (as ExcludedTrait) fn(&self) + "#]], + ); + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl ExcludedTrait for T {} + +struct Foo; +impl Foo { + fn inherent(&self) {} +} + +fn foo() { + ::$0 +} + "#, + expect![[r#" + me bar(…) (as ExcludedTrait) fn(&self) + me baz(…) (as ExcludedTrait) fn(&self) + me foo(…) (as ExcludedTrait) fn(&self) + "#]], + ); +} + +#[test] +fn excluded_trait_not_excluded_when_inherent_path() { + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl ExcludedTrait for T {} + +fn foo() { + ::$0 +} + "#, + expect![[r#" + me bar(…) (as ExcludedTrait) fn(&self) + me baz(…) (as ExcludedTrait) fn(&self) + me foo(…) (as ExcludedTrait) fn(&self) + "#]], + ); + check_with_config( + CompletionConfig { exclude_traits: &["test::ExcludedTrait".to_owned()], ..TEST_CONFIG }, + r#" +trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +impl ExcludedTrait for T {} + +fn foo() { + T::$0 +} + "#, + expect![[r#" + me bar(…) (as ExcludedTrait) fn(&self) + me baz(…) (as ExcludedTrait) fn(&self) + me foo(…) (as ExcludedTrait) fn(&self) + "#]], + ); +} + +#[test] +fn hide_ragennew_synthetic_identifiers() { + check_empty( + r#" +//- minicore: iterator +fn bar() { + for i in [0; 10] { + r$0 + } +} + "#, + expect![[r#" + en Option Option<{unknown}> + en Result Result<{unknown}, {unknown}> + fn bar() fn() + lc i i32 + ma const_format_args!(…) macro_rules! const_format_args + ma format_args!(…) macro_rules! format_args + ma format_args_nl!(…) macro_rules! format_args_nl + ma panic!(…) macro_rules! panic + ma print!(…) macro_rules! print + md core + md result (use core::result) + md rust_2015 (use core::prelude::rust_2015) + md rust_2018 (use core::prelude::rust_2018) + md rust_2021 (use core::prelude::rust_2021) + tt Clone + tt Copy + tt IntoIterator + tt Iterator + ta Result (use core::fmt::Result) + ev Err(…) Err(E) + ev None None + ev Ok(…) Ok(T) + ev Some(…) Some(T) + bt u32 u32 + kw async + kw break + kw const + kw continue + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + 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 + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 447dbc998b56..d413977f7c8f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -3,10 +3,14 @@ use expect_test::{expect, Expect}; use crate::{ context::{CompletionAnalysis, NameContext, NameKind, NameRefKind}, tests::{check_edit, check_edit_with_config, TEST_CONFIG}, + CompletionConfig, }; fn check(ra_fixture: &str, expect: Expect) { - let config = TEST_CONFIG; + check_with_config(TEST_CONFIG, ra_fixture, expect); +} + +fn check_with_config(config: CompletionConfig<'_>, ra_fixture: &str, expect: Expect) { let (db, position) = crate::tests::position(ra_fixture); let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap(); @@ -1762,3 +1766,31 @@ fn function() { expect![""], ); } + +#[test] +fn excluded_trait_item_included_when_exact_match() { + check_with_config( + CompletionConfig { + exclude_traits: &["test::module2::ExcludedTrait".to_owned()], + ..TEST_CONFIG + }, + r#" +mod module2 { + pub trait ExcludedTrait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} + } + + impl ExcludedTrait for T {} +} + +fn foo() { + true.foo$0 +} + "#, + expect![[r#" + me foo() (use module2::ExcludedTrait) fn(&self) + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs index f34f3d0fc2f2..79561a0419f9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs @@ -241,3 +241,75 @@ impl Copy for S where $0 "#, ); } + +#[test] +fn test_is_not_considered_macro() { + check( + r#" +#[rustc_builtin] +pub macro test($item:item) { + /* compiler built-in */ +} + +macro_rules! expand_to_test { + ( $i:ident ) => { + #[test] + fn foo() { $i; } + }; +} + +fn bar() { + let value = 5; + expand_to_test!(v$0); +} + "#, + expect![[r#" + ct CONST Unit + en Enum Enum + fn bar() fn() + fn foo() fn() + fn function() fn() + ma expand_to_test!(…) macro_rules! expand_to_test + ma makro!(…) macro_rules! makro + ma test!(…) macro test + md module + sc STATIC Unit + st Record Record + st Tuple Tuple + st Unit Unit + un Union Union + ev TupleV(…) TupleV(u32) + 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 let + 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 + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index 388af48c68b4..6cfb2231a990 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -1345,7 +1345,7 @@ struct Foo = { let mut x = TEST_CONFIG; x.full_function_signatures = true; x 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 42a80d63b1af..7482491fc634 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 @@ -48,7 +48,7 @@ pub fn callable_for_token( let parent = token.parent()?; let calling_node = parent.ancestors().filter_map(ast::CallableExpr::cast).find(|it| { it.arg_list() - .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start())) + .is_some_and(|it| it.syntax().text_range().contains(token.text_range().start())) })?; callable_for_node(sema, &calling_node, &token) @@ -136,7 +136,7 @@ pub fn generic_def_for_node( let first_arg_is_non_lifetime = generic_arg_list .generic_args() .next() - .map_or(false, |arg| !matches!(arg, ast::GenericArg::LifetimeArg(_))); + .is_some_and(|arg| !matches!(arg, ast::GenericArg::LifetimeArg(_))); Some((def, active_param, first_arg_is_non_lifetime, variant)) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 932ca373020d..2d30bb412735 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -13,9 +13,10 @@ use either::Either; use hir::{ Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType, Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field, - Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro, - Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule, - Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility, + Function, GenericDef, GenericParam, GenericSubstitution, HasContainer, HasVisibility, + HirDisplay, Impl, InlineAsmOperand, ItemContainer, Label, Local, Macro, Module, ModuleDef, + Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule, Trait, TraitAlias, + TupleField, TypeAlias, Variant, VariantDef, Visibility, }; use span::Edition; use stdx::{format_to, impl_from}; @@ -96,9 +97,39 @@ impl Definition { } pub fn enclosing_definition(&self, db: &RootDatabase) -> Option { + fn container_to_definition(container: ItemContainer) -> Option { + match container { + ItemContainer::Trait(it) => Some(it.into()), + ItemContainer::Impl(it) => Some(it.into()), + ItemContainer::Module(it) => Some(it.into()), + ItemContainer::ExternBlock() | ItemContainer::Crate(_) => None, + } + } match self { + Definition::Macro(it) => Some(it.module(db).into()), + Definition::Module(it) => it.parent(db).map(Definition::Module), + Definition::Field(it) => Some(it.parent_def(db).into()), + Definition::Function(it) => container_to_definition(it.container(db)), + Definition::Adt(it) => Some(it.module(db).into()), + Definition::Const(it) => container_to_definition(it.container(db)), + Definition::Static(it) => container_to_definition(it.container(db)), + Definition::Trait(it) => container_to_definition(it.container(db)), + Definition::TraitAlias(it) => container_to_definition(it.container(db)), + Definition::TypeAlias(it) => container_to_definition(it.container(db)), + Definition::Variant(it) => Some(Adt::Enum(it.parent_enum(db)).into()), + Definition::SelfType(it) => Some(it.module(db).into()), Definition::Local(it) => it.parent(db).try_into().ok(), - _ => None, + Definition::GenericParam(it) => Some(it.parent().into()), + Definition::Label(it) => it.parent(db).try_into().ok(), + Definition::ExternCrateDecl(it) => container_to_definition(it.container(db)), + Definition::DeriveHelper(it) => Some(it.derive().module(db).into()), + Definition::InlineAsmOperand(it) => it.parent(db).try_into().ok(), + Definition::BuiltinAttr(_) + | Definition::BuiltinType(_) + | Definition::BuiltinLifetime(_) + | Definition::TupleField(_) + | Definition::ToolModule(_) + | Definition::InlineAsmRegOrRegClass(_) => None, } } @@ -304,7 +335,7 @@ fn find_std_module( let std_crate = famous_defs.std()?; let std_root_module = std_crate.root_module(); std_root_module.children(db).find(|module| { - module.name(db).map_or(false, |module| module.display(db, edition).to_string() == name) + module.name(db).is_some_and(|module| module.display(db, edition).to_string() == name) }) } @@ -359,24 +390,32 @@ impl IdentClass { .or_else(|| NameClass::classify_lifetime(sema, lifetime).map(IdentClass::NameClass)) } - pub fn definitions(self) -> ArrayVec { + pub fn definitions(self) -> ArrayVec<(Definition, Option), 2> { let mut res = ArrayVec::new(); match self { IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => { - res.push(it) + res.push((it, None)) } - IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => { - res.push(Definition::Local(local_def)); - res.push(Definition::Field(field_ref)); + IdentClass::NameClass(NameClass::PatFieldShorthand { + local_def, + field_ref, + adt_subst, + }) => { + res.push((Definition::Local(local_def), None)); + res.push((Definition::Field(field_ref), Some(adt_subst))); } - IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it), - IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => { - res.push(Definition::Local(local_ref)); - res.push(Definition::Field(field_ref)); + IdentClass::NameRefClass(NameRefClass::Definition(it, subst)) => res.push((it, subst)), + IdentClass::NameRefClass(NameRefClass::FieldShorthand { + local_ref, + field_ref, + adt_subst, + }) => { + res.push((Definition::Local(local_ref), None)); + res.push((Definition::Field(field_ref), Some(adt_subst))); } IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => { - res.push(Definition::ExternCrateDecl(decl)); - res.push(Definition::Module(krate.root_module())); + res.push((Definition::ExternCrateDecl(decl), None)); + res.push((Definition::Module(krate.root_module()), None)); } IdentClass::Operator( OperatorClass::Await(func) @@ -384,9 +423,9 @@ impl IdentClass { | OperatorClass::Bin(func) | OperatorClass::Index(func) | OperatorClass::Try(func), - ) => res.push(Definition::Function(func)), + ) => res.push((Definition::Function(func), None)), IdentClass::Operator(OperatorClass::Range(struct0)) => { - res.push(Definition::Adt(Adt::Struct(struct0))) + res.push((Definition::Adt(Adt::Struct(struct0)), None)) } } res @@ -398,12 +437,20 @@ impl IdentClass { IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => { res.push(it) } - IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => { + IdentClass::NameClass(NameClass::PatFieldShorthand { + local_def, + field_ref, + adt_subst: _, + }) => { res.push(Definition::Local(local_def)); res.push(Definition::Field(field_ref)); } - IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it), - IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => { + IdentClass::NameRefClass(NameRefClass::Definition(it, _)) => res.push(it), + IdentClass::NameRefClass(NameRefClass::FieldShorthand { + local_ref, + field_ref, + adt_subst: _, + }) => { res.push(Definition::Local(local_ref)); res.push(Definition::Field(field_ref)); } @@ -437,6 +484,7 @@ pub enum NameClass { PatFieldShorthand { local_def: Local, field_ref: Field, + adt_subst: GenericSubstitution, }, } @@ -446,7 +494,7 @@ impl NameClass { let res = match self { NameClass::Definition(it) => it, NameClass::ConstReference(_) => return None, - NameClass::PatFieldShorthand { local_def, field_ref: _ } => { + NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => { Definition::Local(local_def) } }; @@ -517,10 +565,13 @@ impl NameClass { let pat_parent = ident_pat.syntax().parent(); if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast) { if record_pat_field.name_ref().is_none() { - if let Some((field, _)) = sema.resolve_record_pat_field(&record_pat_field) { + if let Some((field, _, adt_subst)) = + sema.resolve_record_pat_field_with_subst(&record_pat_field) + { return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field, + adt_subst, }); } } @@ -629,10 +680,11 @@ impl OperatorClass { /// reference to point to two different defs. #[derive(Debug)] pub enum NameRefClass { - Definition(Definition), + Definition(Definition, Option), FieldShorthand { local_ref: Local, field_ref: Field, + adt_subst: GenericSubstitution, }, /// The specific situation where we have an extern crate decl without a rename /// Here we have both a declaration and a reference. @@ -657,12 +709,16 @@ impl NameRefClass { let parent = name_ref.syntax().parent()?; if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) { - if let Some((field, local, _)) = sema.resolve_record_field(&record_field) { + if let Some((field, local, _, adt_subst)) = + sema.resolve_record_field_with_substitution(&record_field) + { let res = match local { - None => NameRefClass::Definition(Definition::Field(field)), - Some(local) => { - NameRefClass::FieldShorthand { field_ref: field, local_ref: local } - } + None => NameRefClass::Definition(Definition::Field(field), Some(adt_subst)), + Some(local) => NameRefClass::FieldShorthand { + field_ref: field, + local_ref: local, + adt_subst, + }, }; return Some(res); } @@ -674,44 +730,43 @@ impl NameRefClass { // Only use this to resolve to macro calls for last segments as qualifiers resolve // to modules below. if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { - return Some(NameRefClass::Definition(Definition::Macro(macro_def))); + return Some(NameRefClass::Definition(Definition::Macro(macro_def), None)); } } } - return sema.resolve_path(&path).map(Into::into).map(NameRefClass::Definition); + return sema + .resolve_path_with_subst(&path) + .map(|(res, subst)| NameRefClass::Definition(res.into(), subst)); } match_ast! { match parent { ast::MethodCallExpr(method_call) => { sema.resolve_method_call_fallback(&method_call) - .map(|it| { - it.map_left(Definition::Function) - .map_right(Definition::Field) - .either(NameRefClass::Definition, NameRefClass::Definition) + .map(|(def, subst)| { + match def { + Either::Left(def) => NameRefClass::Definition(def.into(), subst), + Either::Right(def) => NameRefClass::Definition(def.into(), subst), + } }) }, ast::FieldExpr(field_expr) => { sema.resolve_field_fallback(&field_expr) - .map(|it| { - NameRefClass::Definition(match it { - Either::Left(Either::Left(field)) => Definition::Field(field), - Either::Left(Either::Right(field)) => Definition::TupleField(field), - Either::Right(fun) => Definition::Function(fun), + .map(|(def, subst)| { + match def { + Either::Left(Either::Left(def)) => NameRefClass::Definition(def.into(), subst), + Either::Left(Either::Right(def)) => NameRefClass::Definition(Definition::TupleField(def), subst), + Either::Right(def) => NameRefClass::Definition(def.into(), subst), + } }) - }) }, ast::RecordPatField(record_pat_field) => { - sema.resolve_record_pat_field(&record_pat_field) - .map(|(field, ..)|field) - .map(Definition::Field) - .map(NameRefClass::Definition) + sema.resolve_record_pat_field_with_subst(&record_pat_field) + .map(|(field, _, subst)| NameRefClass::Definition(Definition::Field(field), Some(subst))) }, ast::RecordExprField(record_expr_field) => { - sema.resolve_record_field(&record_expr_field) - .map(|(field, ..)|field) - .map(Definition::Field) - .map(NameRefClass::Definition) + sema.resolve_record_field_with_substitution(&record_expr_field) + .map(|(field, _, _, subst)| NameRefClass::Definition(Definition::Field(field), Some(subst))) }, ast::AssocTypeArg(_) => { // `Trait` @@ -728,28 +783,30 @@ impl NameRefClass { }) .find(|alias| alias.name(sema.db).eq_ident(name_ref.text().as_str())) { - return Some(NameRefClass::Definition(Definition::TypeAlias(ty))); + // No substitution, this can only occur in type position. + return Some(NameRefClass::Definition(Definition::TypeAlias(ty), None)); } } None }, ast::UseBoundGenericArgs(_) => { + // No substitution, this can only occur in type position. sema.resolve_use_type_arg(name_ref) .map(GenericParam::TypeParam) .map(Definition::GenericParam) - .map(NameRefClass::Definition) + .map(|it| NameRefClass::Definition(it, None)) }, ast::ExternCrate(extern_crate_ast) => { let extern_crate = sema.to_def(&extern_crate_ast)?; let krate = extern_crate.resolved_crate(sema.db)?; Some(if extern_crate_ast.rename().is_some() { - NameRefClass::Definition(Definition::Module(krate.root_module())) + NameRefClass::Definition(Definition::Module(krate.root_module()), None) } else { NameRefClass::ExternCrateShorthand { krate, decl: extern_crate } }) }, ast::AsmRegSpec(_) => { - Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(()))) + Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(()), None)) }, _ => None } @@ -762,13 +819,17 @@ impl NameRefClass { ) -> Option { let _p = tracing::info_span!("NameRefClass::classify_lifetime", ?lifetime).entered(); if lifetime.text() == "'static" { - return Some(NameRefClass::Definition(Definition::BuiltinLifetime(StaticLifetime))); + return Some(NameRefClass::Definition( + Definition::BuiltinLifetime(StaticLifetime), + None, + )); } let parent = lifetime.syntax().parent()?; match parent.kind() { - SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => { - sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition) - } + SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => sema + .resolve_label(lifetime) + .map(Definition::Label) + .map(|it| NameRefClass::Definition(it, None)), SyntaxKind::LIFETIME_ARG | SyntaxKind::USE_BOUND_GENERIC_ARGS | SyntaxKind::SELF_PARAM @@ -778,7 +839,7 @@ impl NameRefClass { .resolve_lifetime_param(lifetime) .map(GenericParam::LifetimeParam) .map(Definition::GenericParam) - .map(NameRefClass::Definition), + .map(|it| NameRefClass::Definition(it, None)), _ => None, } } @@ -901,3 +962,17 @@ impl TryFrom for Definition { } } } + +impl From for Definition { + fn from(def: GenericDef) -> Self { + match def { + GenericDef::Function(it) => it.into(), + GenericDef::Adt(it) => it.into(), + GenericDef::Trait(it) => it.into(), + GenericDef::TraitAlias(it) => it.into(), + GenericDef::TypeAlias(it) => it.into(), + GenericDef::Impl(it) => it.into(), + GenericDef::Const(it) => it.into(), + } + } +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs index b52a325790b1..a0ef0f90a65b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs @@ -226,9 +226,8 @@ impl HasDocs for hir::AssocItem { impl HasDocs for hir::ExternCrateDecl { fn docs(self, db: &dyn HirDatabase) -> Option { - let crate_docs = - docs_from_attrs(&self.resolved_crate(db)?.root_module().attrs(db)).map(String::from); - let decl_docs = docs_from_attrs(&self.attrs(db)).map(String::from); + let crate_docs = docs_from_attrs(&self.resolved_crate(db)?.root_module().attrs(db)); + let decl_docs = docs_from_attrs(&self.attrs(db)); match (decl_docs, crate_docs) { (None, None) => None, (Some(decl_docs), None) => Some(decl_docs), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index dab36bf20b9b..8f0be1d90354 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -706,7 +706,7 @@ fn path_import_candidate( Some(match qualifier { Some(qualifier) => match sema.resolve_path(&qualifier) { Some(PathResolution::Def(ModuleDef::BuiltinType(_))) | None => { - if qualifier.first_qualifier().map_or(true, |it| sema.resolve_path(&it).is_none()) { + if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) { let qualifier = qualifier .segments() .map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text()))) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index fc86d169a243..8e25ad3472d3 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -72,9 +72,7 @@ impl ImportScope { fn from(syntax: SyntaxNode) -> Option { use syntax::match_ast; fn contains_cfg_attr(attrs: &dyn HasAttrs) -> bool { - attrs - .attrs() - .any(|attr| attr.as_simple_call().map_or(false, |(ident, _)| ident == "cfg")) + attrs.attrs().any(|attr| attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg")) } match_ast! { match syntax { @@ -102,9 +100,7 @@ impl ImportScope { sema: &Semantics<'_, RootDatabase>, ) -> Option { fn contains_cfg_attr(attrs: &dyn HasAttrs) -> bool { - attrs - .attrs() - .any(|attr| attr.as_simple_call().map_or(false, |(ident, _)| ident == "cfg")) + attrs.attrs().any(|attr| attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg")) } // Walk up the ancestor tree searching for a suitable node to do insertions on @@ -487,7 +483,7 @@ fn insert_use_(scope: &ImportScope, use_item: ast::Use, group_imports: bool) { .contains(&token.kind()) } }) - .filter(|child| child.as_token().map_or(true, |t| t.kind() != SyntaxKind::WHITESPACE)) + .filter(|child| child.as_token().is_none_or(|t| t.kind() != SyntaxKind::WHITESPACE)) .last() { cov_mark::hit!(insert_empty_inner_attr); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs index 4c197b453381..9e89dfe87abe 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs @@ -290,7 +290,7 @@ pub fn try_normalize_use_tree_mut( fn recursive_normalize(use_tree: &ast::UseTree, style: NormalizationStyle) -> Option<()> { let use_tree_list = use_tree.use_tree_list()?; let merge_subtree_into_parent_tree = |single_subtree: &ast::UseTree| { - let subtree_is_only_self = single_subtree.path().as_ref().map_or(false, path_is_self); + let subtree_is_only_self = single_subtree.path().as_ref().is_some_and(path_is_self); let merged_path = match (use_tree.path(), single_subtree.path()) { // If the subtree is `{self}` then we cannot merge: `use diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 1f77ea1ec66c..b3105e6524d5 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -320,7 +320,7 @@ impl<'a> Ranker<'a> { let same_text = tok.text() == self.text; // anything that mapped into a token tree has likely no semantic information let no_tt_parent = - tok.parent().map_or(false, |it| it.kind() != parser::SyntaxKind::TOKEN_TREE); + tok.parent().is_some_and(|it| it.kind() != parser::SyntaxKind::TOKEN_TREE); (both_idents as usize) | ((exact_same_kind as usize) << 1) | ((same_text as usize) << 2) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 156f21b784e5..a045c22c2dff 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -285,7 +285,7 @@ impl Ctx<'_> { if path.qualifier().is_some() { return None; } - if path.segment().map_or(false, |s| { + if path.segment().is_some_and(|s| { s.parenthesized_arg_list().is_some() || (s.self_token().is_some() && path.parent_path().is_none()) }) { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index c5215eb3e630..68199dd87118 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -1081,7 +1081,7 @@ impl<'a> FindUsages<'a> { }; match NameRefClass::classify(self.sema, name_ref) { - Some(NameRefClass::Definition(Definition::SelfType(impl_))) + Some(NameRefClass::Definition(Definition::SelfType(impl_), _)) if ty_eq(impl_.self_ty(self.sema.db)) => { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); @@ -1102,7 +1102,7 @@ impl<'a> FindUsages<'a> { sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { match NameRefClass::classify(self.sema, name_ref) { - Some(NameRefClass::Definition(def @ Definition::Module(_))) if def == self.def => { + Some(NameRefClass::Definition(def @ Definition::Module(_), _)) if def == self.def => { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let category = if is_name_ref_in_import(name_ref) { ReferenceCategory::IMPORT @@ -1147,7 +1147,7 @@ impl<'a> FindUsages<'a> { sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { match NameRefClass::classify_lifetime(self.sema, lifetime) { - Some(NameRefClass::Definition(def)) if def == self.def => { + Some(NameRefClass::Definition(def, _)) if def == self.def => { let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); let reference = FileReference { range, @@ -1166,7 +1166,7 @@ impl<'a> FindUsages<'a> { sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { match NameRefClass::classify(self.sema, name_ref) { - Some(NameRefClass::Definition(def)) + Some(NameRefClass::Definition(def, _)) if self.def == def // is our def a trait assoc item? then we want to find all assoc items from trait impls of our trait || matches!(self.assoc_item_container, Some(hir::AssocItemContainer::Trait(_))) @@ -1182,7 +1182,7 @@ impl<'a> FindUsages<'a> { } // FIXME: special case type aliases, we can't filter between impl and trait defs here as we lack the substitutions // so we always resolve all assoc type aliases to both their trait def and impl defs - Some(NameRefClass::Definition(def)) + Some(NameRefClass::Definition(def, _)) if self.assoc_item_container.is_some() && matches!(self.def, Definition::TypeAlias(_)) && convert_to_def_in_trait(self.sema.db, def) @@ -1196,7 +1196,7 @@ impl<'a> FindUsages<'a> { }; sink(file_id, reference) } - Some(NameRefClass::Definition(def)) if self.include_self_kw_refs.is_some() => { + Some(NameRefClass::Definition(def, _)) if self.include_self_kw_refs.is_some() => { if self.include_self_kw_refs == def_to_ty(self.sema, &def) { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let reference = FileReference { @@ -1209,7 +1209,11 @@ impl<'a> FindUsages<'a> { false } } - Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { + Some(NameRefClass::FieldShorthand { + local_ref: local, + field_ref: field, + adt_subst: _, + }) => { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let field = Definition::Field(field); @@ -1240,7 +1244,7 @@ impl<'a> FindUsages<'a> { sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { match NameClass::classify(self.sema, name) { - Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) + Some(NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ }) if matches!( self.def, Definition::Field(_) if Definition::Field(field_ref) == self.def ) => @@ -1352,12 +1356,12 @@ fn is_name_ref_in_import(name_ref: &ast::NameRef) -> bool { .parent() .and_then(ast::PathSegment::cast) .and_then(|it| it.parent_path().top_path().syntax().parent()) - .map_or(false, |it| it.kind() == SyntaxKind::USE_TREE) + .is_some_and(|it| it.kind() == SyntaxKind::USE_TREE) } fn is_name_ref_in_test(sema: &Semantics<'_, RootDatabase>, name_ref: &ast::NameRef) -> bool { name_ref.syntax().ancestors().any(|node| match ast::Fn::cast(node) { - Some(it) => sema.to_def(&it).map_or(false, |func| func.is_test(sema.db)), + Some(it) => sema.to_def(&it).is_some_and(|func| func.is_test(sema.db)), None => false, }) } 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 91e0b4495f5f..74c0b8e2baac 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 @@ -220,7 +220,7 @@ pub fn vis_eq(this: &ast::Visibility, other: &ast::Visibility) -> bool { match (this.kind(), other.kind()) { (VisibilityKind::In(this), VisibilityKind::In(other)) => { stdx::iter_eq_by(this.segments(), other.segments(), |lhs, rhs| { - lhs.kind().zip(rhs.kind()).map_or(false, |it| match it { + lhs.kind().zip(rhs.kind()).is_some_and(|it| match it { (PathSegmentKind::CrateKw, PathSegmentKind::CrateKw) | (PathSegmentKind::SelfKw, PathSegmentKind::SelfKw) | (PathSegmentKind::SuperKw, PathSegmentKind::SuperKw) => true, @@ -259,7 +259,7 @@ pub fn is_pattern_cond(expr: ast::Expr) -> bool { .or_else(|| expr.rhs().map(is_pattern_cond)) .unwrap_or(false) } - ast::Expr::ParenExpr(expr) => expr.expr().map_or(false, is_pattern_cond), + ast::Expr::ParenExpr(expr) => expr.expr().is_some_and(is_pattern_cond), ast::Expr::LetExpr(_) => true, _ => false, } @@ -408,7 +408,7 @@ fn for_each_break_expr( } pub fn eq_label_lt(lt1: &Option, lt2: &Option) -> bool { - lt1.as_ref().zip(lt2.as_ref()).map_or(false, |(lt, lbl)| lt.text() == lbl.text()) + lt1.as_ref().zip(lt2.as_ref()).is_some_and(|(lt, lbl)| lt.text() == lbl.text()) } struct TreeWithDepthIterator { 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 1e08e8e30980..365d726d2a93 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 @@ -2,13 +2,13 @@ use std::{collections::hash_map::Entry, str::FromStr}; -use hir::Semantics; +use hir::{Semantics, SemanticsScope}; use itertools::Itertools; use rustc_hash::FxHashMap; use stdx::to_lower_snake_case; use syntax::{ ast::{self, HasName}, - match_ast, AstNode, Edition, SmolStr, SmolStrBuilder, + match_ast, AstNode, Edition, SmolStr, SmolStrBuilder, ToSmolStr, }; use crate::RootDatabase; @@ -100,6 +100,19 @@ impl NameGenerator { generator } + pub fn new_from_scope_locals(scope: Option>) -> Self { + let mut generator = Self::new(); + if let Some(scope) = scope { + scope.process_all_names(&mut |name, scope| { + if let hir::ScopeDef::Local(_) = scope { + generator.insert(name.as_str()); + } + }); + } + + generator + } + /// Suggest a name without conflicts. If the name conflicts with existing names, /// it will try to resolve the conflict by adding a numeric suffix. pub fn suggest_name(&mut self, name: &str) -> SmolStr { @@ -162,6 +175,59 @@ impl NameGenerator { self.suggest_name(&c.to_string()) } + /// Suggest name of variable for given expression + /// + /// In current implementation, the function tries to get the name from + /// the following sources: + /// + /// * if expr is an argument to function/method, use parameter name + /// * if expr is a function/method call, use function name + /// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names) + /// * fallback: `var_name` + /// + /// It also applies heuristics to filter out less informative names + /// + /// Currently it sticks to the first name found. + pub fn for_variable( + &mut self, + expr: &ast::Expr, + sema: &Semantics<'_, RootDatabase>, + ) -> SmolStr { + // `from_param` does not benefit from stripping it need the largest + // context possible so we check firstmost + if let Some(name) = from_param(expr, sema) { + return self.suggest_name(&name); + } + + let mut next_expr = Some(expr.clone()); + while let Some(expr) = next_expr { + let name = from_call(&expr) + .or_else(|| from_type(&expr, sema)) + .or_else(|| from_field_name(&expr)); + if let Some(name) = name { + return self.suggest_name(&name); + } + + match expr { + ast::Expr::RefExpr(inner) => next_expr = inner.expr(), + ast::Expr::AwaitExpr(inner) => next_expr = inner.expr(), + // ast::Expr::BlockExpr(block) => expr = block.tail_expr(), + ast::Expr::CastExpr(inner) => next_expr = inner.expr(), + ast::Expr::MethodCallExpr(method) if is_useless_method(&method) => { + next_expr = method.receiver(); + } + ast::Expr::ParenExpr(inner) => next_expr = inner.expr(), + ast::Expr::TryExpr(inner) => next_expr = inner.expr(), + ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(ast::UnaryOp::Deref) => { + next_expr = prefix.expr() + } + _ => break, + } + } + + self.suggest_name("var_name") + } + /// Insert a name into the pool fn insert(&mut self, name: &str) { let (prefix, suffix) = Self::split_numeric_suffix(name); @@ -191,63 +257,8 @@ impl NameGenerator { } } -/// Suggest name of variable for given expression -/// -/// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name. -/// I.e. it doesn't look for names in scope. -/// -/// # Current implementation -/// -/// In current implementation, the function tries to get the name from -/// the following sources: -/// -/// * if expr is an argument to function/method, use parameter name -/// * if expr is a function/method call, use function name -/// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names) -/// * fallback: `var_name` -/// -/// It also applies heuristics to filter out less informative names -/// -/// Currently it sticks to the first name found. -// FIXME: Microoptimize and return a `SmolStr` here. -pub fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { - // `from_param` does not benefit from stripping - // it need the largest context possible - // so we check firstmost - if let Some(name) = from_param(expr, sema) { - return name; - } - - let mut next_expr = Some(expr.clone()); - while let Some(expr) = next_expr { - let name = - from_call(&expr).or_else(|| from_type(&expr, sema)).or_else(|| from_field_name(&expr)); - if let Some(name) = name { - return name; - } - - match expr { - ast::Expr::RefExpr(inner) => next_expr = inner.expr(), - ast::Expr::AwaitExpr(inner) => next_expr = inner.expr(), - // ast::Expr::BlockExpr(block) => expr = block.tail_expr(), - ast::Expr::CastExpr(inner) => next_expr = inner.expr(), - ast::Expr::MethodCallExpr(method) if is_useless_method(&method) => { - next_expr = method.receiver(); - } - ast::Expr::ParenExpr(inner) => next_expr = inner.expr(), - ast::Expr::TryExpr(inner) => next_expr = inner.expr(), - ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(ast::UnaryOp::Deref) => { - next_expr = prefix.expr() - } - _ => break, - } - } - - "var_name".to_owned() -} - -fn normalize(name: &str) -> Option { - let name = to_lower_snake_case(name); +fn normalize(name: &str) -> Option { + let name = to_lower_snake_case(name).to_smolstr(); if USELESS_NAMES.contains(&name.as_str()) { return None; @@ -280,11 +291,11 @@ fn is_useless_method(method: &ast::MethodCallExpr) -> bool { } } -fn from_call(expr: &ast::Expr) -> Option { +fn from_call(expr: &ast::Expr) -> Option { from_func_call(expr).or_else(|| from_method_call(expr)) } -fn from_func_call(expr: &ast::Expr) -> Option { +fn from_func_call(expr: &ast::Expr) -> Option { let call = match expr { ast::Expr::CallExpr(call) => call, _ => return None, @@ -297,7 +308,7 @@ fn from_func_call(expr: &ast::Expr) -> Option { normalize(ident.text()) } -fn from_method_call(expr: &ast::Expr) -> Option { +fn from_method_call(expr: &ast::Expr) -> Option { let method = match expr { ast::Expr::MethodCallExpr(call) => call, _ => return None, @@ -319,7 +330,7 @@ fn from_method_call(expr: &ast::Expr) -> Option { normalize(name) } -fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option { +fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option { let arg_list = expr.syntax().parent().and_then(ast::ArgList::cast)?; let args_parent = arg_list.syntax().parent()?; let func = match_ast! { @@ -338,7 +349,7 @@ fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option Option { @@ -350,7 +361,7 @@ fn var_name_from_pat(pat: &ast::Pat) -> Option { } } -fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option { +fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option { let ty = sema.type_of_expr(expr)?.adjusted(); let ty = ty.remove_ref().unwrap_or(ty); let edition = sema.scope(expr.syntax())?.krate().edition(sema.db); @@ -358,7 +369,7 @@ fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option Option { +fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option { let name = if let Some(adt) = ty.as_adt() { let name = adt.name(db).display(db, edition).to_string(); @@ -393,7 +404,7 @@ fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Optio Some(name) } -fn from_field_name(expr: &ast::Expr) -> Option { +fn from_field_name(expr: &ast::Expr) -> Option { let field = match expr { ast::Expr::FieldExpr(field) => field, _ => return None, @@ -424,7 +435,7 @@ mod tests { frange.range, "selection is not an expression(yet contained in one)" ); - let name = for_variable(&expr, &sema); + let name = NameGenerator::new().for_variable(&expr, &sema); assert_eq!(&name, expected); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs index a319a0bcf6d6..2b59c1a22f6f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs @@ -34,6 +34,8 @@ fn describe_reason(reason: GenericArgsProhibitedReason) -> String { return "you can specify generic arguments on either the enum or the variant, but not both" .to_owned(); } + GenericArgsProhibitedReason::Const => "constants", + GenericArgsProhibitedReason::Static => "statics", }; format!("generic arguments are not allowed on {kind}") } @@ -435,6 +437,169 @@ type T = bool; impl Trait for () { type Assoc = i32; // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types +} + "#, + ); + } + + #[test] + fn in_record_expr() { + check_diagnostics( + r#" +mod foo { + pub struct Bar { pub field: i32 } +} +fn baz() { + let _ = foo::<()>::Bar { field: 0 }; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn in_record_pat() { + check_diagnostics( + r#" +mod foo { + pub struct Bar { field: i32 } +} +fn baz(v: foo::Bar) { + let foo::<()>::Bar { .. } = v; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn in_tuple_struct_pat() { + check_diagnostics( + r#" +mod foo { + pub struct Bar(i32); +} +fn baz(v: foo::Bar) { + let foo::<()>::Bar(..) = v; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn in_path_pat() { + check_diagnostics( + r#" +mod foo { + pub struct Bar; +} +fn baz(v: foo::Bar) { + let foo::<()>::Bar = v; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn in_path_expr() { + check_diagnostics( + r#" +mod foo { + pub struct Bar; +} +fn baz() { + let _ = foo::<()>::Bar; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn const_and_static() { + check_diagnostics( + r#" +const CONST: i32 = 0; +static STATIC: i32 = 0; +fn baz() { + let _ = CONST::<()>; + // ^^^^^^ 💡 error: generic arguments are not allowed on constants + let _ = STATIC::<()>; + // ^^^^^^ 💡 error: generic arguments are not allowed on statics +} + "#, + ); + } + + #[test] + fn enum_variant() { + check_diagnostics( + r#" +enum Enum { + Variant(A), +} +mod enum_ { + pub(super) use super::Enum::Variant as V; +} +fn baz() { + let v = Enum::<()>::Variant::<()>(()); + // ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both + let Enum::<()>::Variant::<()>(..) = v; + // ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both + let _ = Enum::<()>::Variant(()); + let _ = Enum::Variant::<()>(()); +} +fn foo() { + use Enum::Variant; + let _ = Variant::<()>(()); + let _ = enum_::V::<()>(()); + let _ = enum_::<()>::V::<()>(()); + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn dyn_trait() { + check_diagnostics( + r#" +mod foo { + pub trait Trait {} +} + +fn bar() { + let _: &dyn foo::<()>::Trait; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + let _: &foo::<()>::Trait; + // ^^^^^^ 💡 error: generic arguments are not allowed on modules +} + "#, + ); + } + + #[test] + fn regression_18768() { + check_diagnostics( + r#" +//- minicore: result +//- /foo.rs crate:foo edition:2018 +pub mod lib { + mod core { + pub use core::*; + } + pub use self::core::result; +} + +pub mod __private { + pub use crate::lib::result::Result::{self, Err, Ok}; +} + +//- /bar.rs crate:bar deps:foo edition:2018 +fn bar() { + _ = foo::__private::Result::<(), ()>::Ok; } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 5f38d13570a5..8117401a5342 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -237,6 +237,24 @@ fn main() { fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { check_diagnostics( r#" +#[rustc_intrinsic] +pub fn bitreverse(x: u32) -> u32; // Safe intrinsic +#[rustc_intrinsic] +pub unsafe fn floorf32(x: f32) -> f32; // Unsafe intrinsic + +fn main() { + let _ = bitreverse(12); + let _ = floorf32(12.0); + //^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block +} +"#, + ); + } + + #[test] + fn no_missing_unsafe_diagnostic_with_legacy_safe_intrinsic() { + check_diagnostics( + r#" extern "rust-intrinsic" { #[rustc_safe_intrinsic] pub fn bitreverse(x: u32) -> u32; // Safe intrinsic diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index 121a463c9f15..6a4e5ba290ec 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -316,6 +316,11 @@ fn main() { }, kind: Bin, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, @@ -401,6 +406,11 @@ fn main() { }, kind: Bin, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, @@ -537,6 +547,11 @@ fn main() { }, kind: Bin, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, @@ -597,6 +612,11 @@ fn main() {} }, kind: Bin, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, @@ -709,6 +729,11 @@ fn main() { }, kind: Bin, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, @@ -744,6 +769,20 @@ mod tests { "#, expect![[r#" [ + Annotation { + range: 3..7, + kind: HasReferences { + pos: FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 3, + }, + data: Some( + [], + ), + }, + }, Annotation { range: 3..7, kind: Runnable( @@ -760,23 +799,14 @@ mod tests { }, kind: Bin, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, - Annotation { - range: 3..7, - kind: HasReferences { - pos: FilePositionWrapper { - file_id: FileId( - 0, - ), - offset: 3, - }, - data: Some( - [], - ), - }, - }, Annotation { range: 18..23, kind: Runnable( @@ -796,6 +826,11 @@ mod tests { path: "tests", }, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, @@ -822,6 +857,11 @@ mod tests { }, }, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), }, diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs index e5b4ed17b2a4..8066894cd837 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -47,7 +47,7 @@ pub(crate) fn incoming_calls( .find_nodes_at_offset_with_descend(file, offset) .filter_map(move |node| match node { ast::NameLike::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? { - NameRefClass::Definition(def @ Definition::Function(_)) => Some(def), + NameRefClass::Definition(def @ Definition::Function(_), _) => Some(def), _ => None, }, ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? { diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index ea16a11d56d0..72fcac54177f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -147,8 +147,8 @@ pub(crate) fn external_docs( let definition = match_ast! { match node { ast::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? { - NameRefClass::Definition(def) => def, - NameRefClass::FieldShorthand { local_ref: _, field_ref } => { + NameRefClass::Definition(def, _) => def, + NameRefClass::FieldShorthand { local_ref: _, field_ref, adt_subst: _ } => { Definition::Field(field_ref) } NameRefClass::ExternCrateShorthand { decl, .. } => { @@ -157,7 +157,7 @@ pub(crate) fn external_docs( }, ast::Name(name) => match NameClass::classify(sema, &name)? { NameClass::Definition(it) | NameClass::ConstReference(it) => it, - NameClass::PatFieldShorthand { local_def: _, field_ref } => Definition::Field(field_ref), + NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ } => Definition::Field(field_ref), }, _ => return None } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/intra_doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/intra_doc_links.rs index ebdd4add177e..6cc240d65249 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/intra_doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/intra_doc_links.rs @@ -25,7 +25,7 @@ pub(super) fn parse_intra_doc_link(s: &str) -> (&str, Option) { .find_map(|(ns, (prefixes, suffixes))| { if let Some(prefix) = prefixes.iter().find(|&&prefix| { s.starts_with(prefix) - && s.chars().nth(prefix.len()).map_or(false, |c| c == '@' || c == ' ') + && s.chars().nth(prefix.len()).is_some_and(|c| c == '@' || c == ' ') }) { Some((&s[prefix.len() + 1..], ns)) } else { @@ -41,7 +41,7 @@ pub(super) fn strip_prefixes_suffixes(s: &str) -> &str { .find_map(|(prefixes, suffixes)| { if let Some(prefix) = prefixes.iter().find(|&&prefix| { s.starts_with(prefix) - && s.chars().nth(prefix.len()).map_or(false, |c| c == '@' || c == ' ') + && s.chars().nth(prefix.len()).is_some_and(|c| c == '@' || c == ' ') }) { Some(&s[prefix.len() + 1..]) } else { diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 10a73edd51c5..e028c5ff0cb4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -253,6 +253,7 @@ fn _format( let &crate_id = db.relevant_crates(file_id).iter().next()?; let edition = db.crate_graph()[crate_id].edition; + #[allow(clippy::disallowed_methods)] let mut cmd = std::process::Command::new(toolchain::Tool::Rustfmt.path()); cmd.arg("--edition"); cmd.arg(edition.to_string()); @@ -573,7 +574,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >where { + impl <>core::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} @@ -599,7 +600,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >where{}"#]], + impl <>core::marker::Copy for Foo< >where{}"#]], ); } @@ -614,7 +615,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >where{}"#]], + impl <>core::marker::Copy for Foo< >where{}"#]], ); check( r#" @@ -625,7 +626,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >where { + impl <>core::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs index 9dacbd8badf3..7b6a5ef13e52 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs @@ -36,7 +36,7 @@ pub(crate) fn goto_declaration( let def = match_ast! { match parent { ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? { - NameRefClass::Definition(it) => Some(it), + NameRefClass::Definition(it, _) => Some(it), NameRefClass::FieldShorthand { field_ref, .. } => return field_ref.try_to_nav(db), NameRefClass::ExternCrateShorthand { decl, .. } => diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 363f852e0e4b..6c66907ec3ef 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -103,7 +103,7 @@ pub(crate) fn goto_definition( IdentClass::classify_node(sema, &parent)? .definitions() .into_iter() - .flat_map(|def| { + .flat_map(|(def, _)| { if let Definition::ExternCrateDecl(crate_def) = def { return crate_def .resolved_crate(db) diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index e36c8ee2f3f7..04da1f67e957 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -48,7 +48,7 @@ pub(crate) fn goto_implementation( } ast::NameLike::NameRef(name_ref) => NameRefClass::classify(&sema, name_ref) .and_then(|class| match class { - NameRefClass::Definition(def) => Some(def), + NameRefClass::Definition(def, _) => Some(def), NameRefClass::FieldShorthand { .. } | NameRefClass::ExternCrateShorthand { .. } => None, }), diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 4690416e0596..4002cbebad6c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -307,7 +307,7 @@ fn hl_exit_points( let range = match &expr { ast::Expr::TryExpr(try_) => try_.question_mark_token().map(|token| token.text_range()), ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_) - if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) => + if sema.type_of_expr(&expr).is_some_and(|ty| ty.original.is_never()) => { Some(expr.syntax().text_range()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 332dfacbb43f..1431bd8ca291 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -6,7 +6,7 @@ mod tests; use std::{iter, ops::Not}; use either::Either; -use hir::{db::DefDatabase, HasCrate, HasSource, LangItem, Semantics}; +use hir::{db::DefDatabase, GenericSubstitution, HasCrate, HasSource, LangItem, Semantics}; use ide_db::{ defs::{Definition, IdentClass, NameRefClass, OperatorClass}, famous_defs::FamousDefs, @@ -35,6 +35,14 @@ pub struct HoverConfig { pub max_trait_assoc_items_count: Option, pub max_fields_count: Option, pub max_enum_variants_count: Option, + pub max_subst_ty_len: SubstTyLen, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SubstTyLen { + Unlimited, + LimitTo(usize), + Hide, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -158,7 +166,8 @@ fn hover_offset( if let Some(doc_comment) = token_as_doc_comment(&original_token) { cov_mark::hit!(no_highlight_on_comment_hover); return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| { - let res = hover_for_definition(sema, file_id, def, &node, None, false, config, edition); + let res = + hover_for_definition(sema, file_id, def, None, &node, None, false, config, edition); Some(RangeInfo::new(range, res)) }); } @@ -170,6 +179,7 @@ fn hover_offset( sema, file_id, Definition::from(resolution?), + None, &original_token.parent()?, None, false, @@ -217,7 +227,7 @@ fn hover_offset( { if let Some(macro_) = sema.resolve_macro_call(¯o_call) { break 'a vec![( - Definition::Macro(macro_), + (Definition::Macro(macro_), None), sema.resolve_macro_call_arm(¯o_call), false, node, @@ -236,7 +246,7 @@ fn hover_offset( decl, .. }) => { - vec![(Definition::ExternCrateDecl(decl), None, false, node)] + vec![((Definition::ExternCrateDecl(decl), None), None, false, node)] } class => { @@ -252,12 +262,13 @@ fn hover_offset( } } .into_iter() - .unique_by(|&(def, _, _, _)| def) - .map(|(def, macro_arm, hovered_definition, node)| { + .unique_by(|&((def, _), _, _, _)| def) + .map(|((def, subst), macro_arm, hovered_definition, node)| { hover_for_definition( sema, file_id, def, + subst, &node, macro_arm, hovered_definition, @@ -381,6 +392,7 @@ pub(crate) fn hover_for_definition( sema: &Semantics<'_, RootDatabase>, file_id: FileId, def: Definition, + subst: Option, scope_node: &SyntaxNode, macro_arm: Option, hovered_definition: bool, @@ -408,6 +420,7 @@ pub(crate) fn hover_for_definition( _ => None, }; let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default(); + let subst_types = subst.map(|subst| subst.types(db)); let markup = render::definition( sema.db, @@ -416,6 +429,7 @@ pub(crate) fn hover_for_definition( ¬able_traits, macro_arm, hovered_definition, + subst_types.as_ref(), config, edition, ); @@ -425,7 +439,7 @@ pub(crate) fn hover_for_definition( show_fn_references_action(sema.db, def), show_implementations_action(sema.db, def), runnable_action(sema, def, file_id), - goto_type_action_for_def(sema.db, def, ¬able_traits, edition), + goto_type_action_for_def(sema.db, def, ¬able_traits, subst_types, edition), ] .into_iter() .flatten() @@ -517,6 +531,7 @@ fn goto_type_action_for_def( db: &RootDatabase, def: Definition, notable_traits: &[(hir::Trait, Vec<(Option, hir::Name)>)], + subst_types: Option>, edition: Edition, ) -> Option { let mut targets: Vec = Vec::new(); @@ -554,6 +569,12 @@ fn goto_type_action_for_def( walk_and_push_ty(db, &ty, &mut push_new_def); } + if let Some(subst_types) = subst_types { + for (_, ty) in subst_types { + walk_and_push_ty(db, &ty, &mut push_new_def); + } + } + HoverAction::goto_type_from_targets(db, targets, edition) } 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 e617d462ecd6..8fbd445d9624 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -5,7 +5,7 @@ use either::Either; use hir::{ db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, AssocItemContainer, CaptureKind, DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError, - MethodViolationCode, Name, Semantics, Trait, Type, TypeInfo, + MethodViolationCode, Name, Semantics, Symbol, Trait, Type, TypeInfo, VariantDef, }; use ide_db::{ base_db::SourceDatabase, @@ -27,7 +27,7 @@ use syntax::{algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxToken, T} use crate::{ doc_links::{remove_links, rewrite_links}, - hover::{notable_traits, walk_and_push_ty}, + hover::{notable_traits, walk_and_push_ty, SubstTyLen}, interpret::render_const_eval_error, HoverAction, HoverConfig, HoverResult, Markup, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, @@ -274,7 +274,7 @@ pub(super) fn keyword( let markup = process_markup( sema.db, Definition::Module(doc_owner), - &markup(Some(docs.into()), description, None, None), + &markup(Some(docs.into()), description, None, None, String::new()), config, ); Some(HoverResult { markup, actions }) @@ -336,8 +336,8 @@ pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option Option { match def { - Definition::Field(f) => Some(f.parent_def(db).name(db)), + Definition::Field(f) => { + let parent = f.parent_def(db); + let parent_name = parent.name(db); + let parent_name = parent_name.display(db, edition).to_string(); + return match parent { + VariantDef::Variant(variant) => { + let enum_name = variant.parent_enum(db).name(db); + Some(format!("{}::{parent_name}", enum_name.display(db, edition))) + } + _ => Some(parent_name), + }; + } Definition::Local(l) => l.parent(db).name(db), Definition::Variant(e) => Some(e.parent_enum(db).name(db)), @@ -421,6 +432,7 @@ pub(super) fn definition( notable_traits: &[(Trait, Vec<(Option, Name)>)], macro_arm: Option, hovered_definition: bool, + subst_types: Option<&Vec<(Symbol, Type)>>, config: &HoverConfig, edition: Edition, ) -> Markup { @@ -582,12 +594,21 @@ pub(super) fn definition( _ => None, }; + let variance_info = || match def { + Definition::GenericParam(it) => it.variance(db).as_ref().map(ToString::to_string), + _ => None, + }; + let mut extra = String::new(); if hovered_definition { if let Some(notable_traits) = render_notable_trait(db, notable_traits, edition) { extra.push_str("\n___\n"); extra.push_str(¬able_traits); } + if let Some(variance_info) = variance_info() { + extra.push_str("\n___\n"); + extra.push_str(&variance_info); + } if let Some(layout_info) = layout_info() { extra.push_str("\n___\n"); extra.push_str(&layout_info); @@ -604,7 +625,38 @@ pub(super) fn definition( desc.push_str(&value); } - markup(docs.map(Into::into), desc, extra.is_empty().not().then_some(extra), mod_path) + let subst_types = match config.max_subst_ty_len { + SubstTyLen::Hide => String::new(), + SubstTyLen::LimitTo(_) | SubstTyLen::Unlimited => { + let limit = if let SubstTyLen::LimitTo(limit) = config.max_subst_ty_len { + Some(limit) + } else { + None + }; + subst_types + .map(|subst_type| { + subst_type + .iter() + .filter(|(_, ty)| !ty.is_unknown()) + .format_with(", ", |(name, ty), fmt| { + fmt(&format_args!( + "`{name}` = `{}`", + ty.display_truncated(db, limit, edition) + )) + }) + .to_string() + }) + .unwrap_or_default() + } + }; + + markup( + docs.map(Into::into), + desc, + extra.is_empty().not().then_some(extra), + mod_path, + subst_types, + ) } pub(super) fn literal( @@ -663,10 +715,22 @@ pub(super) fn literal( let mut s = format!("```rust\n{ty}\n```\n___\n\n"); match value { Ok(value) => { + let backtick_len = value.chars().filter(|c| *c == '`').count(); + let spaces_len = value.chars().filter(|c| *c == ' ').count(); + let backticks = "`".repeat(backtick_len + 1); + let space_char = if spaces_len == value.len() { "" } else { " " }; + if let Some(newline) = value.find('\n') { - format_to!(s, "value of literal (truncated up to newline): {}", &value[..newline]) + format_to!( + s, + "value of literal (truncated up to newline): {backticks}{space_char}{}{space_char}{backticks}", + &value[..newline] + ) } else { - format_to!(s, "value of literal: {value}") + format_to!( + s, + "value of literal: {backticks}{space_char}{value}{space_char}{backticks}" + ) } } Err(error) => format_to!(s, "invalid literal: {error}"), @@ -831,12 +895,11 @@ fn closure_ty( } else { String::new() }; - let mut markup = format!("```rust\n{}", c.display_with_id(sema.db, edition)); + let mut markup = format!("```rust\n{}\n```", c.display_with_impl(sema.db, edition)); if let Some(trait_) = c.fn_trait(sema.db).get_id(sema.db, original.krate(sema.db).into()) { push_new_def(hir::Trait::from(trait_).into()) } - format_to!(markup, "\n{}\n```", c.display_with_impl(sema.db, edition),); if let Some(layout) = render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None) { @@ -872,6 +935,7 @@ fn markup( rust: String, extra: Option, mod_path: Option, + subst_types: String, ) -> Markup { let mut buf = String::new(); @@ -886,6 +950,10 @@ fn markup( buf.push_str(&extra); } + if !subst_types.is_empty() { + format_to!(buf, "\n___\n{subst_types}"); + } + if let Some(doc) = docs { format_to!(buf, "\n___\n\n{}", doc); } @@ -901,7 +969,7 @@ fn find_std_module( let std_crate = famous_defs.std()?; let std_root_module = std_crate.root_module(); std_root_module.children(db).find(|module| { - module.name(db).map_or(false, |module| module.display(db, edition).to_string() == name) + module.name(db).is_some_and(|module| module.display(db, edition).to_string() == name) }) } 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 50d0d4c5df65..2e7637e46773 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -20,6 +20,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { max_trait_assoc_items_count: None, max_fields_count: Some(5), max_enum_variants_count: Some(5), + max_subst_ty_len: super::SubstTyLen::Unlimited, }; fn check_hover_no_result(ra_fixture: &str) { @@ -347,7 +348,6 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} impl Fn(i32) -> i32 ``` ___ @@ -371,7 +371,6 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} impl Fn(i32) -> i32 ``` ___ @@ -406,7 +405,6 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} impl FnOnce() ``` ___ @@ -436,7 +434,6 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} impl FnMut() ``` ___ @@ -462,7 +459,6 @@ fn main() { "#, expect![[r#" ```rust - {closure#0} impl FnOnce() -> S2 ``` ___ @@ -2321,6 +2317,53 @@ fn foo(Foo { b$0ar }: &Foo) {} ) } +#[test] +fn test_hover_show_type_def_for_subst() { + check_actions( + r#" +fn f(t: T) { + +} + +struct S; + +fn test() { + let a = S; + f$0(a); +} +"#, + expect![[r#" + [ + Reference( + FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 3, + }, + ), + GoToType( + [ + HoverGotoTypeData { + mod_path: "ra_test_fixture::S", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 20..29, + focus_range: 27..28, + name: "S", + kind: Struct, + description: "struct S", + }, + }, + ], + ), + ] + "#]], + ); +} + #[test] fn test_hover_non_ascii_space_doc() { check( @@ -2969,7 +3012,6 @@ fn main() { expect![[r#" *|* ```rust - {closure#0} impl Fn(i32) -> i32 ``` @@ -3212,6 +3254,11 @@ fn foo_$0test() {} }, }, cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, }, ), ] @@ -3229,28 +3276,33 @@ mod tests$0 { } "#, expect![[r#" - [ - Runnable( - Runnable { - use_name_in_title: false, - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 0..46, - focus_range: 4..9, - name: "tests", - kind: Module, - description: "mod tests", - }, - kind: TestMod { - path: "tests", - }, - cfg: None, + [ + Runnable( + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..46, + focus_range: 4..9, + name: "tests", + kind: Module, + description: "mod tests", }, - ), - ] - "#]], + kind: TestMod { + path: "tests", + }, + cfg: None, + update_test: UpdateTest { + expect_test: false, + insta: false, + snapbox: false, + }, + }, + ), + ] + "#]], ); } @@ -4720,7 +4772,7 @@ fn hover_type_param_sized_bounds() { //- minicore: sized trait Trait {} struct Foo(T); -impl Foo {} +impl Foo {} "#, expect![[r#" *T* @@ -4735,7 +4787,7 @@ impl Foo {} //- minicore: sized trait Trait {} struct Foo(T); -impl Foo {} +impl Foo {} "#, expect![[r#" *T* @@ -4763,6 +4815,10 @@ fn foo() {} ```rust T ``` + + --- + + invariant "#]], ); } @@ -4780,6 +4836,10 @@ fn foo() {} ```rust T ``` + + --- + + invariant "#]], ); } @@ -4797,6 +4857,10 @@ fn foo() {} ```rust T: ?Sized ``` + + --- + + invariant "#]], ); } @@ -4815,6 +4879,10 @@ fn foo() {} ```rust T: Trait ``` + + --- + + invariant "#]], ); } @@ -4833,6 +4901,10 @@ fn foo() {} ```rust T: Trait ``` + + --- + + invariant "#]], ); } @@ -4851,6 +4923,10 @@ fn foo() {} ```rust T: Trait + ?Sized ``` + + --- + + invariant "#]], ); } @@ -4868,6 +4944,10 @@ fn foo() {} ```rust T ``` + + --- + + invariant "#]], ); } @@ -4886,6 +4966,10 @@ fn foo() {} ```rust T: Trait ``` + + --- + + invariant "#]], ); } @@ -5176,6 +5260,10 @@ fn main() { --- + `Self` = `()` + + --- + false "#]], ); @@ -5208,6 +5296,10 @@ fn main() { --- + `Self` = `i32` + + --- + false "#]], ); @@ -7285,7 +7377,7 @@ enum Enum { *field* ```rust - ra_test_fixture::RecordV + ra_test_fixture::Enum::RecordV ``` ```rust @@ -8116,7 +8208,7 @@ fn main() { ``` ___ - value of literal: 🦀🦀\A + value of literal: ` 🦀🦀\A ` "#]], ); check( @@ -8132,7 +8224,7 @@ fn main() { ``` ___ - value of literal: 🦀\u{1f980}\\\x41 + value of literal: ` 🦀\u{1f980}\\\x41 ` "#]], ); check( @@ -8154,7 +8246,7 @@ fsdghs"; ``` ___ - value of literal (truncated up to newline): 🦀\u{1f980}\\\x41 + value of literal (truncated up to newline): ` 🦀\u{1f980}\\\x41 ` "#]], ); } @@ -8174,11 +8266,76 @@ fn main() { ``` ___ - value of literal: 🦀🦀\A + value of literal: ` 🦀🦀\A ` "#]], ); } +#[test] +fn rawstring_literal() { + check( + r#" +fn main() { + $0r"`[^`]*`"; +}"#, + expect![[r#" + *r"`[^`]*`"* + ```rust + &str + ``` + ___ + + value of literal: ```` `[^`]*` ```` + "#]], + ); + check( + r#" +fn main() { + $0r"`"; +}"#, + expect![[r#" + *r"`"* + ```rust + &str + ``` + ___ + + value of literal: `` ` `` + "#]], + ); + check( + r#" +fn main() { + $0r" "; +}"#, + expect![[r#" + *r" "* + ```rust + &str + ``` + ___ + + value of literal: ` ` + "#]], + ); + check( + r#" +fn main() { + $0r" Hello World "; + +}"#, + expect![[r#" + *r" Hello World "* + ```rust + &str + ``` + ___ + + value of literal: ` Hello World ` +"#]], + ) +} + #[test] fn byte_string_literal() { check( @@ -8194,7 +8351,7 @@ fn main() { ``` ___ - value of literal: [240, 159, 166, 128, 92] + value of literal: ` [240, 159, 166, 128, 92] ` "#]], ); check( @@ -8210,7 +8367,7 @@ fn main() { ``` ___ - value of literal: [92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92] + value of literal: ` [92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92] ` "#]], ); } @@ -8230,7 +8387,7 @@ fn main() { ``` ___ - value of literal: 0xF0 + value of literal: ` 0xF0 ` "#]], ); check( @@ -8246,7 +8403,7 @@ fn main() { ``` ___ - value of literal: 0x5C + value of literal: ` 0x5C ` "#]], ); } @@ -8266,7 +8423,7 @@ fn main() { ``` ___ - value of literal: A + value of literal: ` A ` "#]], ); check( @@ -8282,7 +8439,7 @@ fn main() { ``` ___ - value of literal: \ + value of literal: ` \ ` "#]], ); check( @@ -8298,7 +8455,7 @@ fn main() { ``` ___ - value of literal: 🦀 + value of literal: ` 🦀 ` "#]], ); } @@ -8318,7 +8475,7 @@ fn main() { ``` ___ - value of literal: 1 (bits: 0x3FF0000000000000) + value of literal: ` 1 (bits: 0x3FF0000000000000) ` "#]], ); check( @@ -8334,7 +8491,7 @@ fn main() { ``` ___ - value of literal: 1 (bits: 0x3C00) + value of literal: ` 1 (bits: 0x3C00) ` "#]], ); check( @@ -8350,7 +8507,7 @@ fn main() { ``` ___ - value of literal: 1 (bits: 0x3F800000) + value of literal: ` 1 (bits: 0x3F800000) ` "#]], ); check( @@ -8366,7 +8523,7 @@ fn main() { ``` ___ - value of literal: 1 (bits: 0x3FFF0000000000000000000000000000) + value of literal: ` 1 (bits: 0x3FFF0000000000000000000000000000) ` "#]], ); check( @@ -8382,7 +8539,7 @@ fn main() { ``` ___ - value of literal: 134000000000000 (bits: 0x42DE77D399980000) + value of literal: ` 134000000000000 (bits: 0x42DE77D399980000) ` "#]], ); check( @@ -8398,7 +8555,7 @@ fn main() { ``` ___ - value of literal: 1523527134274733600000000 (bits: 0x44F429E9249F629B) + value of literal: ` 1523527134274733600000000 (bits: 0x44F429E9249F629B) ` "#]], ); check( @@ -8434,7 +8591,7 @@ fn main() { ``` ___ - value of literal: 34325236457856836345234 (0x744C659178614489D92|0b111010001001100011001011001000101111000011000010100010010001001110110010010) + value of literal: ` 34325236457856836345234 (0x744C659178614489D92|0b111010001001100011001011001000101111000011000010100010010001001110110010010) ` "#]], ); check( @@ -8450,7 +8607,7 @@ fn main() { ``` ___ - value of literal: 13412342421 (0x31F701A95|0b1100011111011100000001101010010101) + value of literal: ` 13412342421 (0x31F701A95|0b1100011111011100000001101010010101) ` "#]], ); check( @@ -8466,7 +8623,7 @@ fn main() { ``` ___ - value of literal: 306328611 (0x12423423|0b10010010000100011010000100011) + value of literal: ` 306328611 (0x12423423|0b10010010000100011010000100011) ` "#]], ); check( @@ -8482,7 +8639,7 @@ fn main() { ``` ___ - value of literal: 255 (0xFF|0b11111111) + value of literal: ` 255 (0xFF|0b11111111) ` "#]], ); check( @@ -8498,7 +8655,7 @@ fn main() { ``` ___ - value of literal: 5349 (0x14E5|0b1010011100101) + value of literal: ` 5349 (0x14E5|0b1010011100101) ` "#]], ); check( @@ -9501,3 +9658,539 @@ fn main() { "#]], ); } + +#[test] +fn subst_fn() { + check( + r#" +struct Foo(T); +impl Foo { + fn foo(v: T, u: U) {} +} + +fn bar() { + Foo::fo$0o(123, false); +} + "#, + expect![[r#" + *foo* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + impl Foo + fn foo(v: T, u: U) + ``` + + --- + + `T` = `i32`, `U` = `bool` + "#]], + ); + check( + r#" +fn foo(v: T) {} + +fn bar() { + fo$0o(123); +} + "#, + expect![[r#" + *foo* + + ```rust + ra_test_fixture + ``` + + ```rust + fn foo(v: T) + ``` + + --- + + `T` = `i32` + "#]], + ); +} + +#[test] +fn subst_record_constructor() { + check( + r#" +struct Foo { field: T } + +fn bar() { + let v = $0Foo { field: 123 }; +} + "#, + expect![[r#" + *Foo* + + ```rust + ra_test_fixture + ``` + + ```rust + struct Foo { + field: T, + } + ``` + + --- + + `T` = `i32` + "#]], + ); + check( + r#" +struct Foo { field: T } + +fn bar() { + let v = Foo { field: 123 }; + let $0Foo { field: _ } = v; +} + "#, + expect![[r#" + *Foo* + + ```rust + ra_test_fixture + ``` + + ```rust + struct Foo { + field: T, + } + ``` + + --- + + `T` = `i32` + "#]], + ); +} + +#[test] +fn subst_method_call() { + check( + r#" +struct Foo(T); + +impl Foo { + fn bar(self, v: T) {} +} + +fn baz() { + Foo(123).bar$0("hello"); +} + "#, + expect![[r#" + *bar* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + impl Foo + fn bar(self, v: T) + ``` + + --- + + `U` = `i32`, `T` = `&str` + "#]], + ); +} + +#[test] +fn subst_type_alias_do_not_work() { + // It is very hard to support subst for type aliases properly in all places because they are eagerly evaluated. + // We can show the user the subst for the underlying type instead but that'll be very confusing. + check( + r#" +struct Foo { a: T, b: U } +type Alias = Foo; + +fn foo() { + let _ = Alias$0 { a: true, b: 123 }; +} + "#, + expect![[r#" + *Alias* + + ```rust + ra_test_fixture + ``` + + ```rust + type Alias = Foo + ``` + "#]], + ); +} + +#[test] +fn subst_self() { + check( + r#" +trait Trait { + fn foo(&self, v: U) {} +} +struct Struct(T); +impl Trait for Struct {} + +fn bar() { + Struct(123).foo$0(true); +} + "#, + expect![[r#" + *foo* + + ```rust + ra_test_fixture::Trait + ``` + + ```rust + trait Trait + fn foo(&self, v: U) + ``` + + --- + + `Self` = `Struct`, `T` = `i64`, `U` = `bool` + "#]], + ); +} + +#[test] +fn subst_with_lifetimes_and_consts() { + check( + r#" +struct Foo<'a, const N: usize, T>(&[T; N]); + +impl<'a, T, const N: usize> Foo<'a, N, T> { + fn foo<'b, const Z: u32, U>(&self, v: U) {} +} + +fn bar() { + Foo(&[1i8]).fo$0o::<456, _>(""); +} + "#, + expect![[r#" + *foo* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + impl<'a, T, const N: usize> Foo<'a, N, T> + fn foo<'b, const Z: u32, U>(&self, v: U) + ``` + + --- + + `T` = `i8`, `U` = `&str` + "#]], + ); +} + +#[test] +fn subst_field() { + check( + r#" +struct Foo { field: T } + +fn bar() { + let v = Foo { $0field: 123 }; +} + "#, + expect![[r#" + *field* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + field: T + ``` + + --- + + `T` = `i32` + "#]], + ); + check( + r#" +struct Foo { field: T } + +fn bar() { + let field = 123; + let v = Foo { field$0 }; +} + "#, + expect![[r#" + *field* + + ```rust + let field: i32 + ``` + --- + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + field: T + ``` + + --- + + `T` = `i32` + "#]], + ); + check( + r#" +struct Foo { field: T } + +fn bar() { + let v = Foo { field: 123 }; + let Foo { field$0 } = v; +} + "#, + expect![[r#" + *field* + + ```rust + let field: i32 + ``` + + --- + + size = 4, align = 4 + --- + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + field: T + ``` + + --- + + `T` = `i32` + "#]], + ); + check( + r#" +struct Foo { field: T } + +fn bar() { + let v = Foo { field: 123 }; + let Foo { field$0: _ } = v; +} + "#, + expect![[r#" + *field* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + field: T + ``` + + --- + + `T` = `i32` + "#]], + ); + check( + r#" +struct Foo { field: T } + +fn bar() { + let v = Foo { field: 123 }; + let _ = (&v).$0field; +} + "#, + expect![[r#" + *field* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + field: T + ``` + + --- + + `T` = `i32` + "#]], + ); + check( + r#" +struct Foo(T); + +fn bar() { + let v = Foo(123); + let _ = v.$00; +} + "#, + expect![[r#" + *0* + + ```rust + ra_test_fixture::Foo + ``` + + ```rust + 0: T + ``` + + --- + + `T` = `i32` + "#]], + ); +} + +#[test] +fn i128_max() { + check( + r#" +//- /core.rs library crate:core +#![rustc_coherence_is_core] +impl u128 { + pub const MAX: Self = 340_282_366_920_938_463_463_374_607_431_768_211_455u128; +} +impl i128 { + pub const MAX: Self = (u128::MAX >> 1) as Self; +} + +//- /foo.rs crate:foo deps:core +fn foo() { + let _ = i128::MAX$0; +} + "#, + expect![ + r#" + *MAX* + + ```rust + core + ``` + + ```rust + pub const MAX: Self = 170141183460469231731687303715884105727 (0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + ``` + "# + ], + ); +} + +#[test] +fn test_runnables_with_snapshot_tests() { + check_actions( + r#" +//- /lib.rs crate:foo deps:expect_test,insta,snapbox +use expect_test::expect; +use insta::assert_debug_snapshot; +use snapbox::Assert; + +#[test] +fn test$0() { + let actual = "new25"; + expect!["new25"].assert_eq(&actual); + Assert::new() + .action_env("SNAPSHOTS") + .eq(actual, snapbox::str!["new25"]); + assert_debug_snapshot!(actual); +} + +//- /lib.rs crate:expect_test +struct Expect; + +impl Expect { + fn assert_eq(&self, actual: &str) {} +} + +#[macro_export] +macro_rules! expect { + ($e:expr) => Expect; // dummy +} + +//- /lib.rs crate:insta +#[macro_export] +macro_rules! assert_debug_snapshot { + ($e:expr) => {}; // dummy +} + +//- /lib.rs crate:snapbox +pub struct Assert; + +impl Assert { + pub fn new() -> Self { Assert } + + pub fn action_env(&self, env: &str) -> &Self { self } + + pub fn eq(&self, actual: &str, expected: &str) {} +} + +#[macro_export] +macro_rules! str { + ($e:expr) => ""; // dummy +} + "#, + expect![[r#" + [ + Reference( + FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 92, + }, + ), + Runnable( + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 81..301, + focus_range: 92..96, + name: "test", + kind: Function, + }, + kind: Test { + test_id: Path( + "test", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + update_test: UpdateTest { + expect_test: true, + insta: true, + snapbox: true, + }, + }, + ), + ] + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index aa99ba49bc83..faa65019eea5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -856,4 +856,18 @@ fn main() { }"#, ); } + + #[test] + fn regression_18840() { + check( + r#" +//- proc_macros: issue_18840 +#[proc_macros::issue_18840] +fn foo() { + let + loop {} +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 4d7d6e270e0a..4e48baa6f146 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -313,7 +313,7 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, // - `dummy_expr` is the original expression wrapped in the operator we want (`*`/`.*`) // - `expr` is the clone of the original expression (with `dummy_expr` as the parent) - let needs_outer_parens = parent.map_or(false, |p| dummy_expr.needs_parens_in(p)); + let needs_outer_parens = parent.is_some_and(|p| dummy_expr.needs_parens_in(p)); let needs_inner_parens = expr.needs_parens_in(dummy_expr.syntax().clone()); (needs_outer_parens, needs_inner_parens) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs index cfe8657fd05e..5afb98cb1c74 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs @@ -61,7 +61,6 @@ pub(super) fn hints( } hint.label.append_str(r); }); - hint.pad_right = was_mut_last; let acc_base = acc.len(); match pat { ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => { @@ -86,6 +85,7 @@ pub(super) fn hints( } ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => { hint.label.append_str("("); + was_mut_last = false; acc.push(InlayHint::closing_paren_after( InlayKind::BindingMode, pat.syntax().text_range(), @@ -94,6 +94,7 @@ pub(super) fn hints( _ => (), } if !hint.label.parts.is_empty() { + hint.pad_right = was_mut_last; acc.push(hint); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs index cd77c3ec3e90..8f2949cb3879 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs @@ -32,7 +32,7 @@ pub(super) fn enum_hints( return None; } // data carrying enums without a primitive repr have no stable discriminants - if data_carrying && def.repr(sema.db).map_or(true, |r| r.int.is_none()) { + if data_carrying && def.repr(sema.db).is_none_or(|r| r.int.is_none()) { return None; } for variant in enum_.variant_list()?.variants() { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index 28b0fa6dd4d1..a03ff6a52b4c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -153,7 +153,7 @@ fn is_param_name_suffix_of_fn_name( .len() .checked_sub(param_name.len()) .and_then(|at| function.is_char_boundary(at).then(|| function.split_at(at))) - .map_or(false, |(prefix, suffix)| { + .is_some_and(|(prefix, suffix)| { suffix.eq_ignore_ascii_case(param_name) && prefix.ends_with('_') }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index c13fc843568c..6e7c718953cc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -86,7 +86,7 @@ pub use crate::{ highlight_related::{HighlightRelatedConfig, HighlightedRange}, hover::{ HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult, - MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, + MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, SubstTyLen, }, inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, @@ -96,8 +96,8 @@ pub use crate::{ join_lines::JoinLinesConfig, markup::Markup, moniker::{ - MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation, - SymbolInformationKind, + Moniker, MonikerDescriptorKind, MonikerIdentifier, MonikerKind, MonikerResult, + PackageInformation, SymbolInformationKind, }, move_item::Direction, navigation_target::{NavigationTarget, TryToNav, UpmappingResult}, @@ -671,19 +671,17 @@ impl Analysis { /// Computes completions at the given position. pub fn completions( &self, - config: &CompletionConfig, + config: &CompletionConfig<'_>, position: FilePosition, trigger_character: Option, ) -> Cancellable>> { - self.with_db(|db| { - ide_completion::completions(db, config, position, trigger_character).map(Into::into) - }) + self.with_db(|db| ide_completion::completions(db, config, position, trigger_character)) } /// Resolves additional completion data at the position given. pub fn resolve_completion_edits( &self, - config: &CompletionConfig, + config: &CompletionConfig<'_>, position: FilePosition, imports: impl IntoIterator + std::panic::UnwindSafe, ) -> Cancellable> { diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 14781b212969..052466725fa1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -3,7 +3,7 @@ use core::fmt; -use hir::{Adt, AsAssocItem, AssocItemContainer, Crate, MacroKind, Semantics}; +use hir::{Adt, AsAssocItem, Crate, HirDisplay, MacroKind, Semantics}; use ide_db::{ base_db::{CrateOrigin, LangCrateOrigin}, defs::{Definition, IdentClass}, @@ -11,6 +11,7 @@ use ide_db::{ FilePosition, RootDatabase, }; use itertools::Itertools; +use span::Edition; use syntax::{AstNode, SyntaxKind::*, T}; use crate::{doc_links::token_as_doc_comment, parent_module::crates_for, RangeInfo}; @@ -57,8 +58,8 @@ pub enum SymbolInformationKind { impl From for MonikerDescriptorKind { fn from(value: SymbolInformationKind) -> Self { match value { - SymbolInformationKind::AssociatedType => Self::TypeParameter, - SymbolInformationKind::Attribute => Self::Macro, + SymbolInformationKind::AssociatedType => Self::Type, + SymbolInformationKind::Attribute => Self::Meta, SymbolInformationKind::Constant => Self::Term, SymbolInformationKind::Enum => Self::Type, SymbolInformationKind::EnumMember => Self::Type, @@ -70,7 +71,7 @@ impl From for MonikerDescriptorKind { SymbolInformationKind::Parameter => Self::Parameter, SymbolInformationKind::SelfParameter => Self::Parameter, SymbolInformationKind::StaticMethod => Self::Method, - SymbolInformationKind::StaticVariable => Self::Meta, + SymbolInformationKind::StaticVariable => Self::Term, SymbolInformationKind::Struct => Self::Type, SymbolInformationKind::Trait => Self::Type, SymbolInformationKind::TraitMethod => Self::Method, @@ -109,10 +110,12 @@ pub enum MonikerKind { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct MonikerResult { - pub identifier: MonikerIdentifier, - pub kind: MonikerKind, - pub package_information: PackageInformation, +pub enum MonikerResult { + /// Uniquely identifies a definition. + Moniker(Moniker), + /// Specifies that the definition is a local, and so does not have a unique identifier. Provides + /// a unique identifier for the container. + Local { enclosing_moniker: Option }, } impl MonikerResult { @@ -121,6 +124,15 @@ impl MonikerResult { } } +/// Information which uniquely identifies a definition which might be referenceable outside of the +/// source file. Visibility declarations do not affect presence. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Moniker { + pub identifier: MonikerIdentifier, + pub kind: MonikerKind, + pub package_information: PackageInformation, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PackageInformation { pub name: String, @@ -232,157 +244,106 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati } } +/// Computes a `MonikerResult` for a definition. Result cases: +/// +/// * `Some(MonikerResult::Moniker(_))` provides a unique `Moniker` which refers to a definition. +/// +/// * `Some(MonikerResult::Local { .. })` provides a `Moniker` for the definition enclosing a local. +/// +/// * `None` is returned for definitions which are not in a module: `BuiltinAttr`, `BuiltinType`, +/// `BuiltinLifetime`, `TupleField`, `ToolModule`, and `InlineAsmRegOrRegClass`. TODO: it might be +/// sensible to provide monikers that refer to some non-existent crate of compiler builtin +/// definitions. pub(crate) fn def_to_moniker( db: &RootDatabase, - def: Definition, + definition: Definition, from_crate: Crate, ) -> Option { - if matches!( - def, - Definition::GenericParam(_) - | Definition::Label(_) - | Definition::DeriveHelper(_) - | Definition::BuiltinAttr(_) - | Definition::ToolModule(_) - ) { - return None; + match definition { + Definition::Local(_) | Definition::Label(_) | Definition::GenericParam(_) => { + return Some(MonikerResult::Local { + enclosing_moniker: enclosing_def_to_moniker(db, definition, from_crate), + }); + } + _ => {} } + Some(MonikerResult::Moniker(def_to_non_local_moniker(db, definition, from_crate)?)) +} - let module = def.module(db)?; +fn enclosing_def_to_moniker( + db: &RootDatabase, + mut def: Definition, + from_crate: Crate, +) -> Option { + loop { + let enclosing_def = def.enclosing_definition(db)?; + if let Some(enclosing_moniker) = def_to_non_local_moniker(db, enclosing_def, from_crate) { + return Some(enclosing_moniker); + } + def = enclosing_def; + } +} + +fn def_to_non_local_moniker( + db: &RootDatabase, + definition: Definition, + from_crate: Crate, +) -> Option { + let module = definition.module(db)?; let krate = module.krate(); let edition = krate.edition(db); - let mut description = vec![]; - description.extend(module.path_to_root(db).into_iter().filter_map(|x| { - Some(MonikerDescriptor { - name: x.name(db)?.display(db, edition).to_string(), - desc: def_to_kind(db, x.into()).into(), - }) - })); - // Handle associated items within a trait - if let Some(assoc) = def.as_assoc_item(db) { - let container = assoc.container(db); - match container { - AssocItemContainer::Trait(trait_) => { - // Because different traits can have functions with the same name, - // we have to include the trait name as part of the moniker for uniqueness. - description.push(MonikerDescriptor { - name: trait_.name(db).display(db, edition).to_string(), - desc: def_to_kind(db, trait_.into()).into(), - }); - } - AssocItemContainer::Impl(impl_) => { - // Because a struct can implement multiple traits, for implementations - // we add both the struct name and the trait name to the path - if let Some(adt) = impl_.self_ty(db).as_adt() { - description.push(MonikerDescriptor { - name: adt.name(db).display(db, edition).to_string(), - desc: def_to_kind(db, adt.into()).into(), + // Add descriptors for this definition and every enclosing definition. + let mut reverse_description = vec![]; + let mut def = definition; + loop { + match def { + Definition::SelfType(impl_) => { + if let Some(trait_ref) = impl_.trait_ref(db) { + // Trait impls use the trait type for the 2nd parameter. + reverse_description.push(MonikerDescriptor { + name: display(db, edition, module, trait_ref), + desc: MonikerDescriptorKind::TypeParameter, }); } - - if let Some(trait_) = impl_.trait_(db) { - description.push(MonikerDescriptor { - name: trait_.name(db).display(db, edition).to_string(), - desc: def_to_kind(db, trait_.into()).into(), + // Both inherent and trait impls use the self type for the first parameter. + reverse_description.push(MonikerDescriptor { + name: display(db, edition, module, impl_.self_ty(db)), + desc: MonikerDescriptorKind::TypeParameter, + }); + reverse_description.push(MonikerDescriptor { + name: "impl".to_owned(), + desc: MonikerDescriptorKind::Type, + }); + } + _ => { + if let Some(name) = def.name(db) { + reverse_description.push(MonikerDescriptor { + name: name.display(db, edition).to_string(), + desc: def_to_kind(db, def).into(), }); + } else if reverse_description.is_empty() { + // Don't allow the last descriptor to be absent. + return None; + } else { + match def { + Definition::Module(module) if module.is_crate_root() => {} + _ => { + tracing::error!(?def, "Encountered enclosing definition with no name"); + } + } } } } + let Some(next_def) = def.enclosing_definition(db) else { + break; + }; + def = next_def; } + reverse_description.reverse(); + let description = reverse_description; - if let Definition::Field(it) = def { - description.push(MonikerDescriptor { - name: it.parent_def(db).name(db).display(db, edition).to_string(), - desc: def_to_kind(db, it.parent_def(db).into()).into(), - }); - } - - // Qualify locals/parameters by their parent definition name. - if let Definition::Local(it) = def { - let parent = Definition::try_from(it.parent(db)).ok(); - if let Some(parent) = parent { - let parent_name = parent.name(db); - if let Some(name) = parent_name { - description.push(MonikerDescriptor { - name: name.display(db, edition).to_string(), - desc: def_to_kind(db, parent).into(), - }); - } - } - } - - let desc = def_to_kind(db, def).into(); - - let name_desc = match def { - // These are handled by top-level guard (for performance). - Definition::GenericParam(_) - | Definition::Label(_) - | Definition::DeriveHelper(_) - | Definition::BuiltinLifetime(_) - | Definition::BuiltinAttr(_) - | Definition::ToolModule(_) - | Definition::InlineAsmRegOrRegClass(_) - | Definition::InlineAsmOperand(_) => return None, - - Definition::Local(local) => { - if !local.is_param(db) { - return None; - } - - MonikerDescriptor { name: local.name(db).display(db, edition).to_string(), desc } - } - Definition::Macro(m) => { - MonikerDescriptor { name: m.name(db).display(db, edition).to_string(), desc } - } - Definition::Function(f) => { - MonikerDescriptor { name: f.name(db).display(db, edition).to_string(), desc } - } - Definition::Variant(v) => { - MonikerDescriptor { name: v.name(db).display(db, edition).to_string(), desc } - } - Definition::Const(c) => { - MonikerDescriptor { name: c.name(db)?.display(db, edition).to_string(), desc } - } - Definition::Trait(trait_) => { - MonikerDescriptor { name: trait_.name(db).display(db, edition).to_string(), desc } - } - Definition::TraitAlias(ta) => { - MonikerDescriptor { name: ta.name(db).display(db, edition).to_string(), desc } - } - Definition::TypeAlias(ta) => { - MonikerDescriptor { name: ta.name(db).display(db, edition).to_string(), desc } - } - Definition::Module(m) => { - MonikerDescriptor { name: m.name(db)?.display(db, edition).to_string(), desc } - } - Definition::BuiltinType(b) => { - MonikerDescriptor { name: b.name().display(db, edition).to_string(), desc } - } - Definition::SelfType(imp) => MonikerDescriptor { - name: imp.self_ty(db).as_adt()?.name(db).display(db, edition).to_string(), - desc, - }, - Definition::Field(it) => { - MonikerDescriptor { name: it.name(db).display(db, edition).to_string(), desc } - } - Definition::TupleField(it) => { - MonikerDescriptor { name: it.name().display(db, edition).to_string(), desc } - } - Definition::Adt(adt) => { - MonikerDescriptor { name: adt.name(db).display(db, edition).to_string(), desc } - } - Definition::Static(s) => { - MonikerDescriptor { name: s.name(db).display(db, edition).to_string(), desc } - } - Definition::ExternCrateDecl(m) => { - MonikerDescriptor { name: m.name(db).display(db, edition).to_string(), desc } - } - }; - - description.push(name_desc); - - Some(MonikerResult { + Some(Moniker { identifier: MonikerIdentifier { crate_name: krate.display_name(db)?.crate_name().to_string(), description, @@ -417,17 +378,57 @@ pub(crate) fn def_to_moniker( }) } +fn display( + db: &RootDatabase, + edition: Edition, + module: hir::Module, + it: T, +) -> String { + match it.display_source_code(db, module.into(), true) { + Ok(result) => result, + // Fallback on display variant that always succeeds + Err(_) => { + let fallback_result = it.display(db, edition).to_string(); + tracing::error!( + display = %fallback_result, "`display_source_code` failed; falling back to using display" + ); + fallback_result + } + } +} + #[cfg(test)] mod tests { - use crate::fixture; + use crate::{fixture, MonikerResult}; use super::MonikerKind; + #[allow(dead_code)] #[track_caller] fn no_moniker(ra_fixture: &str) { let (analysis, position) = fixture::position(ra_fixture); if let Some(x) = analysis.moniker(position).unwrap() { - assert_eq!(x.info.len(), 0, "Moniker founded but no moniker expected: {x:?}"); + assert_eq!(x.info.len(), 0, "Moniker found but no moniker expected: {x:?}"); + } + } + + #[track_caller] + fn check_local_moniker(ra_fixture: &str, identifier: &str, package: &str, kind: MonikerKind) { + let (analysis, position) = fixture::position(ra_fixture); + let x = analysis.moniker(position).unwrap().expect("no moniker found").info; + assert_eq!(x.len(), 1); + match x.into_iter().next().unwrap() { + MonikerResult::Local { enclosing_moniker: Some(x) } => { + assert_eq!(identifier, x.identifier.to_string()); + assert_eq!(package, format!("{:?}", x.package_information)); + assert_eq!(kind, x.kind); + } + MonikerResult::Local { enclosing_moniker: None } => { + panic!("Unexpected local with no enclosing moniker"); + } + MonikerResult::Moniker(_) => { + panic!("Unexpected non-local moniker"); + } } } @@ -436,10 +437,16 @@ mod tests { let (analysis, position) = fixture::position(ra_fixture); let x = analysis.moniker(position).unwrap().expect("no moniker found").info; assert_eq!(x.len(), 1); - let x = x.into_iter().next().unwrap(); - assert_eq!(identifier, x.identifier.to_string()); - assert_eq!(package, format!("{:?}", x.package_information)); - assert_eq!(kind, x.kind); + match x.into_iter().next().unwrap() { + MonikerResult::Local { enclosing_moniker } => { + panic!("Unexpected local enclosed in {:?}", enclosing_moniker); + } + MonikerResult::Moniker(x) => { + assert_eq!(identifier, x.identifier.to_string()); + assert_eq!(package, format!("{:?}", x.package_information)); + assert_eq!(kind, x.kind); + } + } } #[test] @@ -538,15 +545,13 @@ pub mod module { pub trait MyTrait { pub fn func() {} } - struct MyStruct {} - impl MyTrait for MyStruct { pub fn func$0() {} } } "#, - "foo::module::MyStruct::MyTrait::func", + "foo::module::impl::MyStruct::MyTrait::func", r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); @@ -573,8 +578,8 @@ pub struct St { } #[test] - fn no_moniker_for_local() { - no_moniker( + fn local() { + check_local_moniker( r#" //- /lib.rs crate:main deps:foo use foo::module::func; @@ -588,6 +593,9 @@ pub mod module { } } "#, + "foo::module::func", + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, + MonikerKind::Export, ); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs index 74c50fcac352..b51a5cc4f4c1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs +++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs @@ -34,7 +34,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec { let def = match NameClass::classify(sema, &name)? { NameClass::Definition(it) | NameClass::ConstReference(it) => it, - NameClass::PatFieldShorthand { local_def: _, field_ref } => { + NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ } => { Definition::Field(field_ref) } }; @@ -156,10 +156,12 @@ pub(crate) fn find_defs<'a>( let def = match name_like { ast::NameLike::NameRef(name_ref) => { match NameRefClass::classify(sema, &name_ref)? { - NameRefClass::Definition(def) => def, - NameRefClass::FieldShorthand { local_ref, field_ref: _ } => { - Definition::Local(local_ref) - } + NameRefClass::Definition(def, _) => def, + NameRefClass::FieldShorthand { + local_ref, + field_ref: _, + adt_subst: _, + } => Definition::Local(local_ref), NameRefClass::ExternCrateShorthand { decl, .. } => { Definition::ExternCrateDecl(decl) } @@ -167,14 +169,14 @@ pub(crate) fn find_defs<'a>( } ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? { NameClass::Definition(it) | NameClass::ConstReference(it) => it, - NameClass::PatFieldShorthand { local_def, field_ref: _ } => { + NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => { Definition::Local(local_def) } }, ast::NameLike::Lifetime(lifetime) => { NameRefClass::classify_lifetime(sema, &lifetime) .and_then(|class| match class { - NameRefClass::Definition(it) => Some(it), + NameRefClass::Definition(it, _) => Some(it), _ => None, }) .or_else(|| { @@ -203,14 +205,14 @@ fn retain_adt_literal_usages( reference .name .as_name_ref() - .map_or(false, |name_ref| is_enum_lit_name_ref(sema, enum_, name_ref)) + .is_some_and(|name_ref| is_enum_lit_name_ref(sema, enum_, name_ref)) }) }); usages.references.retain(|_, it| !it.is_empty()); } Definition::Adt(_) | Definition::Variant(_) => { refs.for_each(|it| { - it.retain(|reference| reference.name.as_name_ref().map_or(false, is_lit_name_ref)) + it.retain(|reference| reference.name.as_name_ref().is_some_and(is_lit_name_ref)) }); usages.references.retain(|_, it| !it.is_empty()); } diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index a9519c03b322..11bbd99110b4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -227,8 +227,7 @@ fn find_definitions( ast::NameLike::Name(name) if name .syntax() - .parent() - .map_or(false, |it| ast::Rename::can_cast(it.kind())) + .parent().is_some_and(|it| ast::Rename::can_cast(it.kind())) // FIXME: uncomment this once we resolve to usages to extern crate declarations // && name // .syntax() @@ -242,7 +241,7 @@ fn find_definitions( ast::NameLike::Name(name) => NameClass::classify(sema, name) .map(|class| match class { NameClass::Definition(it) | NameClass::ConstReference(it) => it, - NameClass::PatFieldShorthand { local_def, field_ref: _ } => { + NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => { Definition::Local(local_def) } }) @@ -250,8 +249,8 @@ fn find_definitions( ast::NameLike::NameRef(name_ref) => { NameRefClass::classify(sema, name_ref) .map(|class| match class { - NameRefClass::Definition(def) => def, - NameRefClass::FieldShorthand { local_ref, field_ref: _ } => { + NameRefClass::Definition(def, _) => def, + NameRefClass::FieldShorthand { local_ref, field_ref: _, adt_subst: _ } => { Definition::Local(local_ref) } NameRefClass::ExternCrateShorthand { decl, .. } => { @@ -264,8 +263,7 @@ fn find_definitions( .and_then(|def| { // if the name differs from the definitions name it has to be an alias if def - .name(sema.db) - .map_or(false, |it| !it.eq_ident(name_ref.text().as_str())) + .name(sema.db).is_some_and(|it| !it.eq_ident(name_ref.text().as_str())) { Err(format_err!("Renaming aliases is currently unsupported")) } else { @@ -276,7 +274,7 @@ fn find_definitions( ast::NameLike::Lifetime(lifetime) => { NameRefClass::classify_lifetime(sema, lifetime) .and_then(|class| match class { - NameRefClass::Definition(def) => Some(def), + NameRefClass::Definition(def, _) => Some(def), _ => None, }) .or_else(|| { diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index d385e453e213..3e39c750b13b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -1,10 +1,11 @@ -use std::fmt; +use std::{fmt, sync::OnceLock}; +use arrayvec::ArrayVec; use ast::HasName; use cfg::{CfgAtom, CfgExpr}; use hir::{ db::HirDatabase, sym, AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, HirFileIdExt, - Semantics, + ModPath, Name, PathKind, Semantics, Symbol, }; use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_db::{ @@ -15,11 +16,12 @@ use ide_db::{ FilePosition, FxHashMap, FxHashSet, RootDatabase, SymbolKind, }; use itertools::Itertools; +use smallvec::SmallVec; use span::{Edition, TextSize}; use stdx::format_to; use syntax::{ ast::{self, AstNode}, - SmolStr, SyntaxNode, ToSmolStr, + format_smolstr, SmolStr, SyntaxNode, ToSmolStr, }; use crate::{references, FileId, NavigationTarget, ToNav, TryToNav}; @@ -30,6 +32,7 @@ pub struct Runnable { pub nav: NavigationTarget, pub kind: RunnableKind, pub cfg: Option, + pub update_test: UpdateTest, } #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -334,14 +337,20 @@ pub(crate) fn runnable_fn( } }; + let fn_source = sema.source(def)?; let nav = NavigationTarget::from_named( sema.db, - def.source(sema.db)?.as_ref().map(|it| it as &dyn ast::HasName), + fn_source.as_ref().map(|it| it as &dyn ast::HasName), SymbolKind::Function, ) .call_site(); + + let file_range = fn_source.syntax().original_file_range_with_macro_call_body(sema.db); + let update_test = + UpdateTest::find_snapshot_macro(sema, &fn_source.file_syntax(sema.db), file_range); + let cfg = def.attrs(sema.db).cfg(); - Some(Runnable { use_name_in_title: false, nav, kind, cfg }) + Some(Runnable { use_name_in_title: false, nav, kind, cfg, update_test }) } pub(crate) fn runnable_mod( @@ -366,7 +375,22 @@ pub(crate) fn runnable_mod( let attrs = def.attrs(sema.db); let cfg = attrs.cfg(); let nav = NavigationTarget::from_module_to_decl(sema.db, def).call_site(); - Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::TestMod { path }, cfg }) + + let module_source = sema.module_definition_node(def); + let module_syntax = module_source.file_syntax(sema.db); + let file_range = hir::FileRange { + file_id: module_source.file_id.original_file(sema.db), + range: module_syntax.text_range(), + }; + let update_test = UpdateTest::find_snapshot_macro(sema, &module_syntax, file_range); + + Some(Runnable { + use_name_in_title: false, + nav, + kind: RunnableKind::TestMod { path }, + cfg, + update_test, + }) } pub(crate) fn runnable_impl( @@ -392,7 +416,19 @@ pub(crate) fn runnable_impl( test_id.retain(|c| c != ' '); let test_id = TestId::Path(test_id); - Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg }) + let impl_source = sema.source(*def)?; + let impl_syntax = impl_source.syntax(); + let file_range = impl_syntax.original_file_range_with_macro_call_body(sema.db); + let update_test = + UpdateTest::find_snapshot_macro(sema, &impl_syntax.file_syntax(sema.db), file_range); + + Some(Runnable { + use_name_in_title: false, + nav, + kind: RunnableKind::DocTest { test_id }, + cfg, + update_test, + }) } fn has_cfg_test(attrs: AttrsWithOwner) -> bool { @@ -404,6 +440,8 @@ fn runnable_mod_outline_definition( sema: &Semantics<'_, RootDatabase>, def: hir::Module, ) -> Option { + def.as_source_file_id(sema.db)?; + if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db))) { return None; @@ -421,16 +459,22 @@ fn runnable_mod_outline_definition( let attrs = def.attrs(sema.db); let cfg = attrs.cfg(); - if def.as_source_file_id(sema.db).is_some() { - Some(Runnable { - use_name_in_title: false, - nav: def.to_nav(sema.db).call_site(), - kind: RunnableKind::TestMod { path }, - cfg, - }) - } else { - None - } + + let mod_source = sema.module_definition_node(def); + let mod_syntax = mod_source.file_syntax(sema.db); + let file_range = hir::FileRange { + file_id: mod_source.file_id.original_file(sema.db), + range: mod_syntax.text_range(), + }; + let update_test = UpdateTest::find_snapshot_macro(sema, &mod_syntax, file_range); + + Some(Runnable { + use_name_in_title: false, + nav: def.to_nav(sema.db).call_site(), + kind: RunnableKind::TestMod { path }, + cfg, + update_test, + }) } fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { @@ -495,6 +539,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { nav, kind: RunnableKind::DocTest { test_id }, cfg: attrs.cfg(), + update_test: UpdateTest::default(), }; Some(res) } @@ -515,7 +560,7 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool { const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = &["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"]; - docs_from_attrs(attrs).map_or(false, |doc| { + docs_from_attrs(attrs).is_some_and(|doc| { let mut in_code_block = false; for line in doc.lines() { @@ -575,6 +620,128 @@ fn has_test_function_or_multiple_test_submodules( number_of_test_submodules > 1 } +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +pub struct UpdateTest { + pub expect_test: bool, + pub insta: bool, + pub snapbox: bool, +} + +static SNAPSHOT_TEST_MACROS: OnceLock>> = OnceLock::new(); + +impl UpdateTest { + const EXPECT_CRATE: &str = "expect_test"; + const EXPECT_MACROS: &[&str] = &["expect", "expect_file"]; + + const INSTA_CRATE: &str = "insta"; + const INSTA_MACROS: &[&str] = &[ + "assert_snapshot", + "assert_debug_snapshot", + "assert_display_snapshot", + "assert_json_snapshot", + "assert_yaml_snapshot", + "assert_ron_snapshot", + "assert_toml_snapshot", + "assert_csv_snapshot", + "assert_compact_json_snapshot", + "assert_compact_debug_snapshot", + "assert_binary_snapshot", + ]; + + const SNAPBOX_CRATE: &str = "snapbox"; + const SNAPBOX_MACROS: &[&str] = &["assert_data_eq", "file", "str"]; + + fn find_snapshot_macro( + sema: &Semantics<'_, RootDatabase>, + scope: &SyntaxNode, + file_range: hir::FileRange, + ) -> Self { + fn init<'a>( + krate_name: &'a str, + paths: &[&str], + map: &mut FxHashMap<&'a str, Vec>, + ) { + let mut res = Vec::with_capacity(paths.len()); + let krate = Name::new_symbol_root(Symbol::intern(krate_name)); + for path in paths { + let segments = [krate.clone(), Name::new_symbol_root(Symbol::intern(path))]; + let mod_path = ModPath::from_segments(PathKind::Abs, segments); + res.push(mod_path); + } + map.insert(krate_name, res); + } + + let mod_paths = SNAPSHOT_TEST_MACROS.get_or_init(|| { + let mut map = FxHashMap::default(); + init(Self::EXPECT_CRATE, Self::EXPECT_MACROS, &mut map); + init(Self::INSTA_CRATE, Self::INSTA_MACROS, &mut map); + init(Self::SNAPBOX_CRATE, Self::SNAPBOX_MACROS, &mut map); + map + }); + + let search_scope = SearchScope::file_range(file_range); + let find_macro = |paths: &[ModPath]| { + for path in paths { + let Some(items) = sema.resolve_mod_path(scope, path) else { + continue; + }; + for item in items { + if let hir::ItemInNs::Macros(makro) = item { + if Definition::Macro(makro) + .usages(sema) + .in_scope(&search_scope) + .at_least_one() + { + return true; + } + } + } + } + false + }; + + UpdateTest { + expect_test: find_macro(mod_paths.get(Self::EXPECT_CRATE).unwrap()), + insta: find_macro(mod_paths.get(Self::INSTA_CRATE).unwrap()), + snapbox: find_macro(mod_paths.get(Self::SNAPBOX_CRATE).unwrap()), + } + } + + pub fn label(&self) -> Option { + let mut builder: SmallVec<[_; 3]> = SmallVec::new(); + if self.expect_test { + builder.push("Expect"); + } + if self.insta { + builder.push("Insta"); + } + if self.snapbox { + builder.push("Snapbox"); + } + + let res: SmolStr = builder.join(" + ").into(); + if res.is_empty() { + None + } else { + Some(format_smolstr!("↺\u{fe0e} Update Tests ({res})")) + } + } + + pub fn env(&self) -> ArrayVec<(&str, &str), 3> { + let mut env = ArrayVec::new(); + if self.expect_test { + env.push(("UPDATE_EXPECT", "1")); + } + if self.insta { + env.push(("INSTA_UPDATE", "always")); + } + if self.snapbox { + env.push(("SNAPSHOTS", "overwrite")); + } + env + } +} + #[cfg(test)] mod tests { use expect_test::{expect, Expect}; @@ -1337,18 +1504,18 @@ mod tests { file_id: FileId( 0, ), - full_range: 52..115, - focus_range: 67..75, - name: "foo_test", + full_range: 121..185, + focus_range: 136..145, + name: "foo2_test", kind: Function, }, NavigationTarget { file_id: FileId( 0, ), - full_range: 121..185, - focus_range: 136..145, - name: "foo2_test", + full_range: 52..115, + focus_range: 67..75, + name: "foo_test", kind: Function, }, ] diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 516f64959cef..84ccadc8c4ee 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -165,7 +165,7 @@ fn signature_help_for_call( if let Some(callable) = ast::CallableExpr::cast(nodes.next()?) { let inside_callable = callable .arg_list() - .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start())); + .is_some_and(|it| it.syntax().text_range().contains(token.text_range().start())); if inside_callable { break callable; } @@ -650,7 +650,7 @@ fn signature_help_for_tuple_pat_ish( ) -> SignatureHelp { let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_))); let is_left_of_rest_pat = - rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end()); + rest_pat.is_none_or(|it| token.text_range().start() < it.syntax().text_range().end()); let commas = pat .children_with_tokens() 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 0f4b5e7d87a3..700e166b2384 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -13,11 +13,10 @@ use ide_db::{ use span::Edition; use syntax::{AstNode, SyntaxKind::*, SyntaxNode, TextRange, T}; -use crate::inlay_hints::InlayFieldsToResolve; use crate::navigation_target::UpmappingResult; use crate::{ - hover::hover_for_definition, - inlay_hints::AdjustmentHintsMode, + hover::{hover_for_definition, SubstTyLen}, + inlay_hints::{AdjustmentHintsMode, InlayFieldsToResolve}, moniker::{def_to_kind, def_to_moniker, MonikerResult, SymbolInformationKind}, parent_module::crates_for, Analysis, Fold, HoverConfig, HoverResult, InlayHint, InlayHintsConfig, TryToNav, @@ -49,7 +48,6 @@ pub struct TokenStaticData { pub references: Vec, pub moniker: Option, pub display_name: Option, - pub enclosing_moniker: Option, pub signature: Option, pub kind: SymbolInformationKind, } @@ -186,6 +184,7 @@ impl StaticIndex<'_> { max_trait_assoc_items_count: None, max_fields_count: Some(5), max_enum_variants_count: Some(5), + max_subst_ty_len: SubstTyLen::Unlimited, }; let tokens = tokens.filter(|token| { matches!( @@ -210,6 +209,7 @@ impl StaticIndex<'_> { &sema, file_id, def, + None, &node, None, false, @@ -224,9 +224,6 @@ impl StaticIndex<'_> { display_name: def .name(self.db) .map(|name| name.display(self.db, edition).to_string()), - enclosing_moniker: current_crate - .zip(def.enclosing_definition(self.db)) - .and_then(|(cc, enclosing_def)| def_to_moniker(self.db, enclosing_def, cc)), signature: Some(def.label(self.db, edition)), kind: def_to_kind(self.db, def), }); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 0747d1b404b4..f53f0aec0984 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -346,7 +346,7 @@ fn traverse( macro_highlighter = MacroHighlighter::default(); } Some(item) - if attr_or_derive_item.as_ref().map_or(false, |it| *it.item() == item) => + if attr_or_derive_item.as_ref().is_some_and(|it| *it.item() == item) => { attr_or_derive_item = None; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 3767a3917ce7..4f3d5d9d00c2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -76,7 +76,7 @@ pub(super) fn name_like( Some(IdentClass::NameClass(NameClass::Definition(def))) => { highlight_def(sema, krate, def) | HlMod::Definition } - Some(IdentClass::NameRefClass(NameRefClass::Definition(def))) => { + Some(IdentClass::NameRefClass(NameRefClass::Definition(def, _))) => { highlight_def(sema, krate, def) } // FIXME: Fallback for 'static and '_, as we do not resolve these yet @@ -155,7 +155,7 @@ fn punctuation( if parent .as_ref() .and_then(SyntaxNode::parent) - .map_or(false, |it| it.kind() == MACRO_RULES) => + .is_some_and(|it| it.kind() == MACRO_RULES) => { return HlOperator::Other.into() } @@ -193,7 +193,7 @@ fn keyword( T![for] if parent_matches::(&token) => h | HlMod::ControlFlow, T![unsafe] => h | HlMod::Unsafe, T![const] - if token.parent().map_or(false, |it| { + if token.parent().is_some_and(|it| { matches!( it.kind(), SyntaxKind::CONST @@ -253,14 +253,14 @@ fn highlight_name_ref( && !sema .hir_file_for(name_ref.syntax()) .macro_file() - .map_or(false, |it| it.is_derive_attr_pseudo_expansion(sema.db)) => + .is_some_and(|it| it.is_derive_attr_pseudo_expansion(sema.db)) => { return HlTag::Symbol(SymbolKind::Attribute).into(); } None => return HlTag::UnresolvedReference.into(), }; let mut h = match name_class { - NameRefClass::Definition(def) => { + NameRefClass::Definition(def, _) => { if let Definition::Local(local) = &def { let name = local.name(db); let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); @@ -275,7 +275,7 @@ fn highlight_name_ref( } Definition::Trait(trait_) if trait_.is_unsafe(db) => { if ast::Impl::for_trait_name_ref(&name_ref) - .map_or(false, |impl_| impl_.unsafe_token().is_some()) + .is_some_and(|impl_| impl_.unsafe_token().is_some()) { h |= HlMod::Unsafe; } @@ -550,7 +550,7 @@ pub(super) fn highlight_def( let def_crate = def.krate(db); let is_from_other_crate = def_crate != Some(krate); - let is_from_builtin_crate = def_crate.map_or(false, |def_crate| def_crate.is_builtin(db)); + let is_from_builtin_crate = def_crate.is_some_and(|def_crate| def_crate.is_builtin(db)); let is_builtin = matches!( def, Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_) @@ -688,7 +688,7 @@ fn highlight_name_ref_by_syntax( let h = HlTag::Symbol(SymbolKind::Field); let is_union = ast::FieldExpr::cast(parent) .and_then(|field_expr| sema.resolve_field(&field_expr)) - .map_or(false, |field| match field { + .is_some_and(|field| match field { Either::Left(field) => { matches!(field.parent_def(sema.db), hir::VariantDef::Union(_)) } @@ -764,5 +764,5 @@ fn parents_match(mut node: NodeOrToken, mut kinds: &[Sy } fn parent_matches(token: &SyntaxToken) -> bool { - token.parent().map_or(false, |it| N::can_cast(it.kind())) + token.parent().is_some_and(|it| N::can_cast(it.kind())) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 5583f1bc8df9..0a157c157c38 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -28,7 +28,7 @@ pub(super) fn ra_fixture( expanded: &ast::String, ) -> Option<()> { let active_parameter = ActiveParameter::at_token(sema, expanded.syntax().clone())?; - if !active_parameter.ident().map_or(false, |name| name.text().starts_with("ra_fixture")) { + if !active_parameter.ident().is_some_and(|name| name.text().starts_with("ra_fixture")) { return None; } let value = literal.value().ok()?; @@ -279,9 +279,7 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option None, } diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index d37318ff4570..8998934e0e89 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -174,7 +174,7 @@ fn on_delimited_node_typed( kinds: &[fn(SyntaxKind) -> bool], ) -> Option { let t = reparsed.syntax().token_at_offset(offset).right_biased()?; - if t.prev_token().map_or(false, |t| t.kind().is_any_identifier()) { + if t.prev_token().is_some_and(|t| t.kind().is_any_identifier()) { return None; } let (filter, node) = t diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index c15751e7c680..66b8900109c2 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -361,6 +361,7 @@ define_symbols! { partial_ord, PartialEq, PartialOrd, + CoercePointee, path, Pending, phantom_data, diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 1b2162dad0f7..738994086529 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -14,7 +14,7 @@ use ide_db::{ prime_caches, ChangeWithProcMacros, FxHashMap, RootDatabase, }; use itertools::Itertools; -use proc_macro_api::{MacroDylib, ProcMacroServer}; +use proc_macro_api::{MacroDylib, ProcMacroClient}; use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace}; use span::Span; use vfs::{ @@ -42,7 +42,7 @@ pub fn load_workspace_at( cargo_config: &CargoConfig, load_config: &LoadCargoConfig, progress: &dyn Fn(String), -) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option)> { +) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option)> { let root = AbsPathBuf::assert_utf8(std::env::current_dir()?.join(root)); let root = ProjectManifest::discover_single(&root)?; let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?; @@ -59,7 +59,7 @@ pub fn load_workspace( ws: ProjectWorkspace, extra_env: &FxHashMap, load_config: &LoadCargoConfig, -) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option)> { +) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option)> { let (sender, receiver) = unbounded(); let mut vfs = vfs::Vfs::default(); let mut loader = { @@ -71,10 +71,10 @@ pub fn load_workspace( let proc_macro_server = match &load_config.with_proc_macro_server { ProcMacroServerChoice::Sysroot => ws .find_sysroot_proc_macro_srv() - .and_then(|it| ProcMacroServer::spawn(&it, extra_env).map_err(Into::into)) + .and_then(|it| ProcMacroClient::spawn(&it, extra_env).map_err(Into::into)) .map_err(|e| (e, true)), ProcMacroServerChoice::Explicit(path) => { - ProcMacroServer::spawn(path, extra_env).map_err(Into::into).map_err(|e| (e, true)) + ProcMacroClient::spawn(path, extra_env).map_err(Into::into).map_err(|e| (e, true)) } ProcMacroServerChoice::None => { Err((anyhow::format_err!("proc macro server disabled"), false)) @@ -82,7 +82,7 @@ pub fn load_workspace( }; match &proc_macro_server { Ok(server) => { - tracing::info!(path=%server.path(), "Proc-macro server started") + tracing::info!(path=%server.server_path(), "Proc-macro server started") } Err((e, _)) => { tracing::info!(%e, "Failed to start proc-macro server") @@ -362,7 +362,7 @@ impl SourceRootConfig { /// Load the proc-macros for the given lib path, disabling all expanders whose names are in `ignored_macros`. pub fn load_proc_macro( - server: &ProcMacroServer, + server: &ProcMacroClient, path: &AbsPath, ignored_macros: &[Box], ) -> ProcMacroLoadResult { @@ -476,17 +476,17 @@ struct Expander(proc_macro_api::ProcMacro); impl ProcMacroExpander for Expander { fn expand( &self, - subtree: &tt::Subtree, - attrs: Option<&tt::Subtree>, + subtree: &tt::TopSubtree, + attrs: Option<&tt::TopSubtree>, env: &Env, def_site: Span, call_site: Span, mixed_site: Span, current_dir: Option, - ) -> Result, ProcMacroExpansionError> { + ) -> Result, ProcMacroExpansionError> { match self.0.expand( - subtree, - attrs, + subtree.view(), + attrs.map(|attrs| attrs.view()), env.clone().into(), def_site, call_site, diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs index 270bc05a4ee2..89c300300379 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs @@ -53,11 +53,11 @@ fn benchmark_expand_macro_rules() { .map(|(id, tt)| { let res = rules[&id].expand(&tt, |_| (), DUMMY, Edition::CURRENT); assert!(res.err.is_none()); - res.value.0.token_trees.len() + res.value.0 .0.len() }) .sum() }; - assert_eq!(hash, 65720); + assert_eq!(hash, 450144); } fn macro_rules_fixtures() -> FxHashMap { @@ -68,7 +68,7 @@ fn macro_rules_fixtures() -> FxHashMap { .collect() } -fn macro_rules_fixtures_tt() -> FxHashMap> { +fn macro_rules_fixtures_tt() -> FxHashMap> { let fixture = bench_fixture::numerous_macro_rules(); let source_file = ast::SourceFile::parse(&fixture, span::Edition::CURRENT).ok().unwrap(); @@ -92,7 +92,7 @@ fn macro_rules_fixtures_tt() -> FxHashMap> { /// Generate random invocation fixtures from rules fn invocation_fixtures( rules: &FxHashMap, -) -> Vec<(String, tt::Subtree)> { +) -> Vec<(String, tt::TopSubtree)> { let mut seed = 123456789; let mut res = Vec::new(); @@ -112,19 +112,16 @@ fn invocation_fixtures( // So we just skip any error cases and try again let mut try_cnt = 0; loop { - let mut token_trees = Vec::new(); + let mut builder = tt::TopSubtreeBuilder::new(tt::Delimiter { + open: DUMMY, + close: DUMMY, + kind: tt::DelimiterKind::Invisible, + }); for op in rule.lhs.iter() { - collect_from_op(op, &mut token_trees, &mut seed); + collect_from_op(op, &mut builder, &mut seed); } + let subtree = builder.build(); - let subtree = tt::Subtree { - delimiter: tt::Delimiter { - open: DUMMY, - close: DUMMY, - kind: tt::DelimiterKind::Invisible, - }, - token_trees: token_trees.into_boxed_slice(), - }; if it.expand(&subtree, |_| (), DUMMY, Edition::CURRENT).err.is_none() { res.push((name.clone(), subtree)); break; @@ -139,43 +136,41 @@ fn invocation_fixtures( } return res; - fn collect_from_op(op: &Op, token_trees: &mut Vec>, seed: &mut usize) { + fn collect_from_op(op: &Op, builder: &mut tt::TopSubtreeBuilder, seed: &mut usize) { return match op { Op::Var { kind, .. } => match kind.as_ref() { - Some(MetaVarKind::Ident) => token_trees.push(make_ident("foo")), - Some(MetaVarKind::Ty) => token_trees.push(make_ident("Foo")), - Some(MetaVarKind::Tt) => token_trees.push(make_ident("foo")), - Some(MetaVarKind::Vis) => token_trees.push(make_ident("pub")), - Some(MetaVarKind::Pat) => token_trees.push(make_ident("foo")), - Some(MetaVarKind::Path) => token_trees.push(make_ident("foo")), - Some(MetaVarKind::Literal) => token_trees.push(make_literal("1")), - Some(MetaVarKind::Expr(_)) => token_trees.push(make_ident("foo")), + Some(MetaVarKind::Ident) => builder.push(make_ident("foo")), + Some(MetaVarKind::Ty) => builder.push(make_ident("Foo")), + Some(MetaVarKind::Tt) => builder.push(make_ident("foo")), + Some(MetaVarKind::Vis) => builder.push(make_ident("pub")), + Some(MetaVarKind::Pat) => builder.push(make_ident("foo")), + Some(MetaVarKind::Path) => builder.push(make_ident("foo")), + Some(MetaVarKind::Literal) => builder.push(make_literal("1")), + Some(MetaVarKind::Expr(_)) => builder.push(make_ident("foo")), Some(MetaVarKind::Lifetime) => { - token_trees.push(make_punct('\'')); - token_trees.push(make_ident("a")); - } - Some(MetaVarKind::Block) => { - token_trees.push(make_subtree(tt::DelimiterKind::Brace, None)) + builder.push(make_punct('\'')); + builder.push(make_ident("a")); } + Some(MetaVarKind::Block) => make_subtree(tt::DelimiterKind::Brace, builder), Some(MetaVarKind::Item) => { - token_trees.push(make_ident("fn")); - token_trees.push(make_ident("foo")); - token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None)); - token_trees.push(make_subtree(tt::DelimiterKind::Brace, None)); + builder.push(make_ident("fn")); + builder.push(make_ident("foo")); + make_subtree(tt::DelimiterKind::Parenthesis, builder); + make_subtree(tt::DelimiterKind::Brace, builder); } Some(MetaVarKind::Meta) => { - token_trees.push(make_ident("foo")); - token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None)); + builder.push(make_ident("foo")); + make_subtree(tt::DelimiterKind::Parenthesis, builder); } None => (), Some(kind) => panic!("Unhandled kind {kind:?}"), }, - Op::Literal(it) => token_trees.push(tt::Leaf::from(it.clone()).into()), - Op::Ident(it) => token_trees.push(tt::Leaf::from(it.clone()).into()), + Op::Literal(it) => builder.push(tt::Leaf::from(it.clone())), + Op::Ident(it) => builder.push(tt::Leaf::from(it.clone())), Op::Punct(puncts) => { for punct in puncts.as_slice() { - token_trees.push(tt::Leaf::from(*punct).into()); + builder.push(tt::Leaf::from(*punct)); } } Op::Repeat { tokens, kind, separator } => { @@ -187,20 +182,18 @@ fn invocation_fixtures( }; for i in 0..cnt { for it in tokens.iter() { - collect_from_op(it, token_trees, seed); + collect_from_op(it, builder, seed); } if i + 1 != cnt { if let Some(sep) = separator { match &**sep { Separator::Literal(it) => { - token_trees.push(tt::Leaf::Literal(it.clone()).into()) - } - Separator::Ident(it) => { - token_trees.push(tt::Leaf::Ident(it.clone()).into()) + builder.push(tt::Leaf::Literal(it.clone())) } + Separator::Ident(it) => builder.push(tt::Leaf::Ident(it.clone())), Separator::Puncts(puncts) => { for it in puncts { - token_trees.push(tt::Leaf::Punct(*it).into()) + builder.push(tt::Leaf::Punct(*it)) } } }; @@ -209,15 +202,9 @@ fn invocation_fixtures( } } Op::Subtree { tokens, delimiter } => { - let mut subtree = Vec::new(); - tokens.iter().for_each(|it| { - collect_from_op(it, &mut subtree, seed); - }); - - let subtree = - tt::Subtree { delimiter: *delimiter, token_trees: subtree.into_boxed_slice() }; - - token_trees.push(subtree.into()); + builder.open(delimiter.kind, delimiter.open); + tokens.iter().for_each(|it| collect_from_op(it, builder, seed)); + builder.close(delimiter.close); } Op::Ignore { .. } | Op::Index { .. } @@ -233,35 +220,27 @@ fn invocation_fixtures( *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c); *seed } - fn make_ident(ident: &str) -> tt::TokenTree { + fn make_ident(ident: &str) -> tt::Leaf { tt::Leaf::Ident(tt::Ident { span: DUMMY, sym: Symbol::intern(ident), is_raw: tt::IdentIsRaw::No, }) - .into() } - fn make_punct(char: char) -> tt::TokenTree { - tt::Leaf::Punct(tt::Punct { span: DUMMY, char, spacing: tt::Spacing::Alone }).into() + fn make_punct(char: char) -> tt::Leaf { + tt::Leaf::Punct(tt::Punct { span: DUMMY, char, spacing: tt::Spacing::Alone }) } - fn make_literal(lit: &str) -> tt::TokenTree { + fn make_literal(lit: &str) -> tt::Leaf { tt::Leaf::Literal(tt::Literal { span: DUMMY, symbol: Symbol::intern(lit), kind: tt::LitKind::Str, suffix: None, }) - .into() } - fn make_subtree( - kind: tt::DelimiterKind, - token_trees: Option>>, - ) -> tt::TokenTree { - tt::Subtree { - delimiter: tt::Delimiter { open: DUMMY, close: DUMMY, kind }, - token_trees: token_trees.map(Vec::into_boxed_slice).unwrap_or_default(), - } - .into() + fn make_subtree(kind: tt::DelimiterKind, builder: &mut tt::TopSubtreeBuilder) { + builder.open(kind, DUMMY); + builder.close(DUMMY); } } } diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs index 1979e5171ab0..5539a88c707d 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs @@ -13,12 +13,12 @@ use crate::{parser::MetaVarKind, ExpandError, ExpandErrorKind, ExpandResult, Mat pub(crate) fn expand_rules( rules: &[crate::Rule], - input: &tt::Subtree, + input: &tt::TopSubtree, marker: impl Fn(&mut Span) + Copy, call_site: Span, def_site_edition: Edition, -) -> ExpandResult<(tt::Subtree, MatchedArmIndex)> { - let mut match_: Option<(matcher::Match, &crate::Rule, usize)> = None; +) -> ExpandResult<(tt::TopSubtree, MatchedArmIndex)> { + let mut match_: Option<(matcher::Match<'_>, &crate::Rule, usize)> = None; for (idx, rule) in rules.iter().enumerate() { let new_match = matcher::match_(&rule.lhs, input, def_site_edition); @@ -50,13 +50,7 @@ pub(crate) fn expand_rules( ExpandResult { value: (value, idx.try_into().ok()), err: match_.err.or(transcribe_err) } } else { ExpandResult::new( - ( - tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(call_site), - token_trees: Box::default(), - }, - None, - ), + (tt::TopSubtree::empty(tt::DelimSpan::from_single(call_site)), None), ExpandError::new(call_site, ExpandErrorKind::NoMatchingRule), ) } @@ -107,32 +101,35 @@ pub(crate) fn expand_rules( /// In other words, `Bindings` is a *multi* mapping from `Symbol` to /// `tt::TokenTree`, where the index to select a particular `TokenTree` among /// many is not a plain `usize`, but a `&[usize]`. -#[derive(Debug, Default, Clone, PartialEq, Eq)] -struct Bindings { - inner: FxHashMap, +#[derive(Debug, Default, Clone)] +struct Bindings<'a> { + inner: FxHashMap>, } -#[derive(Debug, Clone, PartialEq, Eq)] -enum Binding { - Fragment(Fragment), - Nested(Vec), +#[derive(Debug, Clone)] +enum Binding<'a> { + Fragment(Fragment<'a>), + Nested(Vec>), Empty, Missing(MetaVarKind), } -#[derive(Debug, Clone, PartialEq, Eq)] -enum Fragment { +#[derive(Debug, Default, Clone)] +enum Fragment<'a> { + #[default] Empty, /// token fragments are just copy-pasted into the output - Tokens(tt::TokenTree), - /// Expr ast fragments are surrounded with `()` on insertion to preserve - /// precedence. Note that this impl is different from the one currently in - /// `rustc` -- `rustc` doesn't translate fragments into token trees at all. + Tokens(tt::TokenTreesView<'a, Span>), + /// Expr ast fragments are surrounded with `()` on transcription to preserve precedence. + /// Note that this impl is different from the one currently in `rustc` -- + /// `rustc` doesn't translate fragments into token trees at all. /// /// At one point in time, we tried to use "fake" delimiters here à la /// proc-macro delimiter=none. As we later discovered, "none" delimiters are /// tricky to handle in the parser, and rustc doesn't handle those either. - Expr(tt::Subtree), + /// + /// The span of the outer delimiters is marked on transcription. + Expr(tt::TokenTreesView<'a, Span>), /// There are roughly two types of paths: paths in expression context, where a /// separator `::` between an identifier and its following generic argument list /// is mandatory, and paths in type context, where `::` can be omitted. @@ -142,5 +139,18 @@ enum Fragment { /// and is trasncribed as an expression-context path, verbatim transcription /// would cause a syntax error. We need to fix it up just before transcribing; /// see `transcriber::fix_up_and_push_path_tt()`. - Path(tt::Subtree), + Path(tt::TokenTreesView<'a, Span>), + TokensOwned(tt::TopSubtree), +} + +impl Fragment<'_> { + fn is_empty(&self) -> bool { + match self { + Fragment::Empty => true, + Fragment::Tokens(it) => it.len() == 0, + Fragment::Expr(it) => it.len() == 0, + Fragment::Path(it) => it.len() == 0, + Fragment::TokensOwned(it) => it.0.is_empty(), + } + } } diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs index 95641abc0f37..b7f25aa38096 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -64,7 +64,10 @@ use std::{rc::Rc, sync::Arc}; use intern::{sym, Symbol}; use smallvec::{smallvec, SmallVec}; use span::{Edition, Span}; -use tt::{iter::TtIter, DelimSpan}; +use tt::{ + iter::{TtElement, TtIter}, + DelimSpan, +}; use crate::{ expander::{Binding, Bindings, ExpandResult, Fragment}, @@ -73,7 +76,7 @@ use crate::{ ExpandError, ExpandErrorKind, MetaTemplate, ValueResult, }; -impl Bindings { +impl<'a> Bindings<'a> { fn push_optional(&mut self, name: Symbol) { self.inner.insert(name, Binding::Fragment(Fragment::Empty)); } @@ -82,14 +85,14 @@ impl Bindings { self.inner.insert(name, Binding::Empty); } - fn bindings(&self) -> impl Iterator { + fn bindings(&self) -> impl Iterator> { self.inner.values() } } -#[derive(Clone, Default, Debug, PartialEq, Eq)] -pub(super) struct Match { - pub(super) bindings: Bindings, +#[derive(Clone, Default, Debug)] +pub(super) struct Match<'a> { + pub(super) bindings: Bindings<'a>, /// We currently just keep the first error and count the rest to compare matches. pub(super) err: Option, pub(super) err_count: usize, @@ -99,7 +102,7 @@ pub(super) struct Match { pub(super) bound_count: usize, } -impl Match { +impl Match<'_> { fn add_err(&mut self, err: ExpandError) { let prev_err = self.err.take(); self.err = prev_err.or(Some(err)); @@ -108,12 +111,16 @@ impl Match { } /// Matching errors are added to the `Match`. -pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree, edition: Edition) -> Match { +pub(super) fn match_<'t>( + pattern: &'t MetaTemplate, + input: &'t tt::TopSubtree, + edition: Edition, +) -> Match<'t> { let mut res = match_loop(pattern, input, edition); res.bound_count = count(res.bindings.bindings()); return res; - fn count<'a>(bindings: impl Iterator) -> usize { + fn count<'a>(bindings: impl Iterator>) -> usize { bindings .map(|it| match it { Binding::Fragment(_) => 1, @@ -126,10 +133,10 @@ pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree, edition: } #[derive(Debug, Clone)] -enum BindingKind { +enum BindingKind<'a> { Empty(Symbol), Optional(Symbol), - Fragment(Symbol, Fragment), + Fragment(Symbol, Fragment<'a>), Missing(Symbol, MetaVarKind), Nested(usize, usize), } @@ -144,12 +151,12 @@ enum LinkNode { } #[derive(Default)] -struct BindingsBuilder { - nodes: Vec>>>, +struct BindingsBuilder<'a> { + nodes: Vec>>>>, nested: Vec>>, } -impl BindingsBuilder { +impl<'a> BindingsBuilder<'a> { fn alloc(&mut self) -> BindingsIdx { let idx = self.nodes.len(); self.nodes.push(Vec::new()); @@ -186,7 +193,7 @@ impl BindingsBuilder { self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Optional(var.clone())))); } - fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &Symbol, fragment: Fragment) { + fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &Symbol, fragment: Fragment<'a>) { self.nodes[idx.0] .push(LinkNode::Node(Rc::new(BindingKind::Fragment(var.clone(), fragment)))); } @@ -207,11 +214,11 @@ impl BindingsBuilder { idx.0 = new_idx; } - fn build(self, idx: &BindingsIdx) -> Bindings { + fn build(self, idx: &BindingsIdx) -> Bindings<'a> { self.build_inner(&self.nodes[idx.0]) } - fn build_inner(&self, link_nodes: &[LinkNode>]) -> Bindings { + fn build_inner(&self, link_nodes: &[LinkNode>>]) -> Bindings<'a> { let mut bindings = Bindings::default(); let mut nodes = Vec::new(); self.collect_nodes(link_nodes, &mut nodes); @@ -257,11 +264,11 @@ impl BindingsBuilder { bindings } - fn collect_nested_ref<'a>( - &'a self, + fn collect_nested_ref<'b>( + &'b self, id: usize, len: usize, - nested_refs: &mut Vec<&'a [LinkNode>]>, + nested_refs: &mut Vec<&'b [LinkNode>>]>, ) { self.nested[id].iter().take(len).for_each(|it| match it { LinkNode::Node(id) => nested_refs.push(&self.nodes[*id]), @@ -269,7 +276,7 @@ impl BindingsBuilder { }); } - fn collect_nested(&self, idx: usize, nested_idx: usize, nested: &mut Vec) { + fn collect_nested(&self, idx: usize, nested_idx: usize, nested: &mut Vec>) { let last = &self.nodes[idx]; let mut nested_refs: Vec<&[_]> = Vec::new(); self.nested[nested_idx].iter().for_each(|it| match *it { @@ -280,17 +287,22 @@ impl BindingsBuilder { nested.extend(nested_refs.into_iter().map(|iter| self.build_inner(iter))); } - fn collect_nodes_ref<'a>(&'a self, id: usize, len: usize, nodes: &mut Vec<&'a BindingKind>) { + fn collect_nodes_ref<'b>( + &'b self, + id: usize, + len: usize, + nodes: &mut Vec<&'b BindingKind<'a>>, + ) { self.nodes[id].iter().take(len).for_each(|it| match it { LinkNode::Node(it) => nodes.push(it), LinkNode::Parent { idx, len } => self.collect_nodes_ref(*idx, *len, nodes), }); } - fn collect_nodes<'a>( - &'a self, - link_nodes: &'a [LinkNode>], - nodes: &mut Vec<&'a BindingKind>, + fn collect_nodes<'b>( + &'b self, + link_nodes: &'b [LinkNode>>], + nodes: &mut Vec<&'b BindingKind<'a>>, ) { link_nodes.iter().for_each(|it| match it { LinkNode::Node(it) => nodes.push(it), @@ -327,7 +339,7 @@ struct MatchState<'t> { bindings: BindingsIdx, /// Cached result of meta variable parsing - meta_result: Option<(TtIter<'t, Span>, ExpandResult>)>, + meta_result: Option<(TtIter<'t, Span>, ExpandResult>>)>, /// Is error occurred in this state, will `poised` to "parent" is_error: bool, @@ -355,8 +367,8 @@ struct MatchState<'t> { fn match_loop_inner<'t>( src: TtIter<'t, Span>, stack: &[TtIter<'t, Span>], - res: &mut Match, - bindings_builder: &mut BindingsBuilder, + res: &mut Match<'t>, + bindings_builder: &mut BindingsBuilder<'t>, cur_items: &mut SmallVec<[MatchState<'t>; 1]>, bb_items: &mut SmallVec<[MatchState<'t>; 1]>, next_items: &mut Vec>, @@ -463,7 +475,7 @@ fn match_loop_inner<'t>( }) } OpDelimited::Op(Op::Subtree { tokens, delimiter }) => { - if let Ok(subtree) = src.clone().expect_subtree() { + if let Ok((subtree, _)) = src.clone().expect_subtree() { if subtree.delimiter.kind == delimiter.kind { item.stack.push(item.dot); item.dot = tokens.iter_delimited_with(*delimiter); @@ -478,8 +490,8 @@ fn match_loop_inner<'t>( match match_res.err { None => { // Some meta variables are optional (e.g. vis) - if match_res.value.is_some() { - item.meta_result = Some((fork, match_res)); + if !match_res.value.is_empty() { + item.meta_result = Some((fork, match_res.map(Some))); try_push!(bb_items, item); } else { bindings_builder.push_optional(&mut item.bindings, name); @@ -489,15 +501,14 @@ fn match_loop_inner<'t>( } Some(err) => { res.add_err(err); - match match_res.value { - Some(fragment) => bindings_builder.push_fragment( + if !match_res.value.is_empty() { + bindings_builder.push_fragment( &mut item.bindings, name, - fragment, - ), - None => { - bindings_builder.push_missing(&mut item.bindings, name, kind) - } + match_res.value, + ) + } else { + bindings_builder.push_missing(&mut item.bindings, name, kind) } item.is_error = true; error_items.push(item); @@ -593,13 +604,13 @@ fn match_loop_inner<'t>( stdx::never!("metavariable expression in lhs found"); } OpDelimited::Open => { - if matches!(src.peek_n(0), Some(tt::TokenTree::Subtree(..))) { + if matches!(src.peek(), Some(TtElement::Subtree(..))) { item.dot.next(); try_push!(next_items, item); } } OpDelimited::Close => { - let is_delim_closed = src.peek_n(0).is_none() && !stack.is_empty(); + let is_delim_closed = src.is_empty() && !stack.is_empty(); if is_delim_closed { item.dot.next(); try_push!(next_items, item); @@ -609,9 +620,13 @@ fn match_loop_inner<'t>( } } -fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, edition: Edition) -> Match { - let span = src.delimiter.delim_span(); - let mut src = TtIter::new(src); +fn match_loop<'t>( + pattern: &'t MetaTemplate, + src: &'t tt::TopSubtree, + edition: Edition, +) -> Match<'t> { + let span = src.top_subtree().delimiter.delim_span(); + let mut src = src.iter(); let mut stack: SmallVec<[TtIter<'_, Span>; 1]> = SmallVec::new(); let mut res = Match::default(); let mut error_recover_item = None; @@ -663,7 +678,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, edition: Edition) // We need to do some post processing after the `match_loop_inner`. // If we reached the EOF, check that there is EXACTLY ONE possible matcher. Otherwise, // either the parse is ambiguous (which should never happen) or there is a syntax error. - if src.peek_n(0).is_none() && stack.is_empty() { + if src.is_empty() && stack.is_empty() { if let [state] = &*eof_items { // remove all errors, because it is the correct answer ! res = Match::default(); @@ -687,11 +702,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, edition: Edition) || !(bb_items.is_empty() || next_items.is_empty()) || bb_items.len() > 1; if has_leftover_tokens { - res.unmatched_tts += src.len(); - while let Some(it) = stack.pop() { - src = it; - res.unmatched_tts += src.len(); - } + res.unmatched_tts += src.remaining().flat_tokens().len(); res.add_err(ExpandError::new(span.open, ExpandErrorKind::LeftoverTokens)); if let Some(error_recover_item) = error_recover_item { @@ -714,9 +725,9 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, edition: Edition) } } else { match src.next() { - Some(tt::TokenTree::Subtree(subtree)) => { + Some(TtElement::Subtree(_, subtree_iter)) => { stack.push(src.clone()); - src = TtIter::new(subtree); + src = subtree_iter; } None => { if let Some(iter) = stack.pop() { @@ -760,18 +771,16 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, edition: Edition) } } -fn match_meta_var( +fn match_meta_var<'t>( kind: MetaVarKind, - input: &mut TtIter<'_, Span>, + input: &mut TtIter<'t, Span>, delim_span: DelimSpan, edition: Edition, -) -> ExpandResult> { +) -> ExpandResult> { let fragment = match kind { MetaVarKind::Path => { return expect_fragment(input, parser::PrefixEntryPoint::Path, edition, delim_span) - .map(|it| { - it.map(|it| tt::TokenTree::subtree_or_wrap(it, delim_span)).map(Fragment::Path) - }); + .map(Fragment::Path); } MetaVarKind::Expr(expr) => { // `expr_2021` should not match underscores, let expressions, or inline const. @@ -782,8 +791,8 @@ fn match_meta_var( // rustc [explicitly checks the next token][1]. // [0]: https://github.com/rust-lang/rust/issues/86730 // [1]: https://github.com/rust-lang/rust/blob/f0c4da499/compiler/rustc_expand/src/mbe/macro_parser.rs#L576 - match input.peek_n(0) { - Some(tt::TokenTree::Leaf(tt::Leaf::Ident(it))) => { + match input.peek() { + Some(TtElement::Leaf(tt::Leaf::Ident(it))) => { let is_err = if it.is_raw.no() && matches!(expr, ExprKind::Expr2021) { it.sym == sym::underscore || it.sym == sym::let_ || it.sym == sym::const_ } else { @@ -799,34 +808,15 @@ fn match_meta_var( _ => {} }; return expect_fragment(input, parser::PrefixEntryPoint::Expr, edition, delim_span) - .map(|tt| { - tt.map(|tt| match tt { - tt::TokenTree::Leaf(leaf) => tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(*leaf.span()), - token_trees: Box::new([leaf.into()]), - }, - tt::TokenTree::Subtree(mut s) => { - if s.delimiter.kind == tt::DelimiterKind::Invisible { - s.delimiter.kind = tt::DelimiterKind::Parenthesis; - } - s - } - }) - .map(Fragment::Expr) - }); + .map(Fragment::Expr); } MetaVarKind::Ident | MetaVarKind::Tt | MetaVarKind::Lifetime | MetaVarKind::Literal => { let span = input.next_span(); - let tt_result = match kind { - MetaVarKind::Ident => input - .expect_ident() - .map(|ident| tt::Leaf::from(ident.clone()).into()) - .map_err(|()| { - ExpandError::binding_error( - span.unwrap_or(delim_span.close), - "expected ident", - ) - }), + let savepoint = input.savepoint(); + let err = match kind { + MetaVarKind::Ident => input.expect_ident().map(drop).map_err(|()| { + ExpandError::binding_error(span.unwrap_or(delim_span.close), "expected ident") + }), MetaVarKind::Tt => expect_tt(input).map_err(|()| { ExpandError::binding_error( span.unwrap_or(delim_span.close), @@ -840,29 +830,19 @@ fn match_meta_var( ) }), MetaVarKind::Literal => { - let neg = eat_char(input, '-'); - input - .expect_literal() - .map(|literal| { - let lit = literal.clone(); - match neg { - None => lit.into(), - Some(neg) => tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(*literal.span()), - token_trees: Box::new([neg, lit.into()]), - }), - } - }) - .map_err(|()| { - ExpandError::binding_error( - span.unwrap_or(delim_span.close), - "expected literal", - ) - }) + eat_char(input, '-'); + input.expect_literal().map(drop).map_err(|()| { + ExpandError::binding_error( + span.unwrap_or(delim_span.close), + "expected literal", + ) + }) } _ => unreachable!(), - }; - return tt_result.map(|it| Some(Fragment::Tokens(it))).into(); + } + .err(); + let tt_result = input.from_savepoint(savepoint); + return ValueResult { value: Fragment::Tokens(tt_result), err }; } MetaVarKind::Ty => parser::PrefixEntryPoint::Ty, MetaVarKind::Pat => parser::PrefixEntryPoint::PatTop, @@ -873,7 +853,7 @@ fn match_meta_var( MetaVarKind::Item => parser::PrefixEntryPoint::Item, MetaVarKind::Vis => parser::PrefixEntryPoint::Vis, }; - expect_fragment(input, fragment, edition, delim_span).map(|it| it.map(Fragment::Tokens)) + expect_fragment(input, fragment, edition, delim_span).map(Fragment::Tokens) } fn collect_vars(collector_fun: &mut impl FnMut(Symbol), pattern: &MetaTemplate) { @@ -990,54 +970,31 @@ fn expect_separator(iter: &mut TtIter<'_, S>, separator: &Separator) -> ok } -fn expect_tt(iter: &mut TtIter<'_, S>) -> Result, ()> { - if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = iter.peek_n(0) { +fn expect_tt(iter: &mut TtIter<'_, S>) -> Result<(), ()> { + if let Some(TtElement::Leaf(tt::Leaf::Punct(punct))) = iter.peek() { if punct.char == '\'' { - expect_lifetime(iter) + expect_lifetime(iter)?; } else { - let puncts = iter.expect_glued_punct()?; - let delimiter = tt::Delimiter { - open: puncts.first().unwrap().span, - close: puncts.last().unwrap().span, - kind: tt::DelimiterKind::Invisible, - }; - let token_trees = puncts.into_iter().map(|p| tt::Leaf::Punct(p).into()).collect(); - Ok(tt::TokenTree::Subtree(tt::Subtree { delimiter, token_trees })) + iter.expect_glued_punct()?; } } else { - iter.next().ok_or(()).cloned() + iter.next().ok_or(())?; } + Ok(()) } -fn expect_lifetime(iter: &mut TtIter<'_, S>) -> Result, ()> { +fn expect_lifetime(iter: &mut TtIter<'_, S>) -> Result<(), ()> { let punct = iter.expect_single_punct()?; if punct.char != '\'' { return Err(()); } - let ident = iter.expect_ident_or_underscore()?; - - Ok(tt::Subtree { - delimiter: tt::Delimiter { - open: punct.span, - close: ident.span, - kind: tt::DelimiterKind::Invisible, - }, - token_trees: Box::new([ - tt::Leaf::Punct(*punct).into(), - tt::Leaf::Ident(ident.clone()).into(), - ]), - } - .into()) + iter.expect_ident_or_underscore()?; + Ok(()) } -fn eat_char(iter: &mut TtIter<'_, S>, c: char) -> Option> { - let mut fork = iter.clone(); - match fork.expect_char(c) { - Ok(_) => { - let tt = iter.next().cloned(); - *iter = fork; - tt - } - Err(_) => None, +fn eat_char(iter: &mut TtIter<'_, S>, c: char) { + if matches!(iter.peek(), Some(TtElement::Leaf(tt::Leaf::Punct(tt::Punct { char, .. }))) if *char == c) + { + iter.next().expect("already peeked"); } } diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs index 1db2f35d2623..9255c5a6899b 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs @@ -3,7 +3,7 @@ use intern::{sym, Symbol}; use span::{Edition, Span}; -use tt::Delimiter; +use tt::{iter::TtElement, Delimiter, TopSubtreeBuilder}; use crate::{ expander::{Binding, Bindings, Fragment}, @@ -11,8 +11,8 @@ use crate::{ ExpandError, ExpandErrorKind, ExpandResult, MetaTemplate, }; -impl Bindings { - fn get(&self, name: &Symbol, span: Span) -> Result<&Binding, ExpandError> { +impl<'t> Bindings<'t> { + fn get(&self, name: &Symbol, span: Span) -> Result<&Binding<'t>, ExpandError> { match self.inner.get(name) { Some(binding) => Ok(binding), None => Err(ExpandError::new( @@ -28,7 +28,7 @@ impl Bindings { mut span: Span, nesting: &mut [NestingState], marker: impl Fn(&mut Span), - ) -> Result { + ) -> Result, ExpandError> { macro_rules! binding_err { ($($arg:tt)*) => { ExpandError::binding_error(span, format!($($arg)*)) }; } @@ -50,86 +50,61 @@ impl Bindings { }; } match b { - Binding::Fragment(f @ (Fragment::Path(sub) | Fragment::Expr(sub))) => { - let tt::Subtree { delimiter, token_trees } = sub; - marker(&mut span); - let subtree = tt::Subtree { - delimiter: tt::Delimiter { - // FIXME split span - open: span, - close: span, - kind: delimiter.kind, - }, - token_trees: token_trees.clone(), - }; - Ok(match f { - Fragment::Tokens(_) | Fragment::Empty => unreachable!(), - Fragment::Expr(_) => Fragment::Expr, - Fragment::Path(_) => Fragment::Path, - }(subtree)) - } - Binding::Fragment(it @ (Fragment::Tokens(_) | Fragment::Empty)) => Ok(it.clone()), + Binding::Fragment(f) => Ok(f.clone()), // emit some reasonable default expansion for missing bindings, // this gives better recovery than emitting the `$fragment-name` verbatim Binding::Missing(it) => Ok({ marker(&mut span); + let mut builder = TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(span)); match it { MetaVarKind::Stmt => { - Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { + builder.push(tt::Leaf::Punct(tt::Punct { span, char: ';', spacing: tt::Spacing::Alone, - }))) + })); + } + MetaVarKind::Block => { + builder.open(tt::DelimiterKind::Brace, span); + builder.close(span); } - MetaVarKind::Block => Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter { - open: span, - close: span, - kind: tt::DelimiterKind::Brace, - }, - token_trees: Box::new([]), - })), // FIXME: Meta and Item should get proper defaults - MetaVarKind::Meta | MetaVarKind::Item | MetaVarKind::Tt | MetaVarKind::Vis => { - Fragment::Empty - } + MetaVarKind::Meta | MetaVarKind::Item | MetaVarKind::Tt | MetaVarKind::Vis => {} MetaVarKind::Path | MetaVarKind::Ty | MetaVarKind::Pat | MetaVarKind::PatParam | MetaVarKind::Expr(_) | MetaVarKind::Ident => { - Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + builder.push(tt::Leaf::Ident(tt::Ident { sym: sym::missing.clone(), span, is_raw: tt::IdentIsRaw::No, - }))) + })); } MetaVarKind::Lifetime => { - Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(span), - token_trees: Box::new([ - tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { - char: '\'', - span, - spacing: tt::Spacing::Joint, - })), - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - sym: sym::missing.clone(), - span, - is_raw: tt::IdentIsRaw::No, - })), - ]), - })) + builder.extend([ + tt::Leaf::Punct(tt::Punct { + char: '\'', + span, + spacing: tt::Spacing::Joint, + }), + tt::Leaf::Ident(tt::Ident { + sym: sym::missing.clone(), + span, + is_raw: tt::IdentIsRaw::No, + }), + ]); } MetaVarKind::Literal => { - Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + builder.push(tt::Leaf::Ident(tt::Ident { sym: sym::missing.clone(), span, is_raw: tt::IdentIsRaw::No, - }))) + })); } } + Fragment::TokensOwned(builder.build()) }), Binding::Nested(_) => { Err(binding_err!("expected simple binding, found nested binding `{name}`")) @@ -143,13 +118,13 @@ impl Bindings { pub(super) fn transcribe( template: &MetaTemplate, - bindings: &Bindings, + bindings: &Bindings<'_>, marker: impl Fn(&mut Span) + Copy, call_site: Span, -) -> ExpandResult> { +) -> ExpandResult> { let mut ctx = ExpandCtx { bindings, nesting: Vec::new(), call_site }; - let mut arena: Vec> = Vec::new(); - expand_subtree(&mut ctx, template, None, &mut arena, marker) + let mut builder = tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(ctx.call_site)); + expand_subtree(&mut ctx, template, &mut builder, marker).map(|()| builder.build()) } #[derive(Debug)] @@ -165,103 +140,97 @@ struct NestingState { #[derive(Debug)] struct ExpandCtx<'a> { - bindings: &'a Bindings, + bindings: &'a Bindings<'a>, nesting: Vec, call_site: Span, } +fn expand_subtree_with_delimiter( + ctx: &mut ExpandCtx<'_>, + template: &MetaTemplate, + builder: &mut tt::TopSubtreeBuilder, + delimiter: Option>, + marker: impl Fn(&mut Span) + Copy, +) -> ExpandResult<()> { + let delimiter = delimiter.unwrap_or_else(|| tt::Delimiter::invisible_spanned(ctx.call_site)); + builder.open(delimiter.kind, delimiter.open); + let result = expand_subtree(ctx, template, builder, marker); + builder.close(delimiter.close); + result +} + fn expand_subtree( ctx: &mut ExpandCtx<'_>, template: &MetaTemplate, - delimiter: Option>, - arena: &mut Vec>, + builder: &mut tt::TopSubtreeBuilder, marker: impl Fn(&mut Span) + Copy, -) -> ExpandResult> { - // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation - let start_elements = arena.len(); +) -> ExpandResult<()> { let mut err = None; 'ops: for op in template.iter() { match op { - Op::Literal(it) => arena.push( - tt::Leaf::from({ - let mut it = it.clone(); - marker(&mut it.span); - it - }) - .into(), - ), - Op::Ident(it) => arena.push( - tt::Leaf::from({ - let mut it = it.clone(); - marker(&mut it.span); - it - }) - .into(), - ), + Op::Literal(it) => builder.push(tt::Leaf::from({ + let mut it = it.clone(); + marker(&mut it.span); + it + })), + Op::Ident(it) => builder.push(tt::Leaf::from({ + let mut it = it.clone(); + marker(&mut it.span); + it + })), Op::Punct(puncts) => { - for punct in puncts.as_slice() { - arena.push( - tt::Leaf::from({ - let mut it = *punct; - marker(&mut it.span); - it - }) - .into(), - ); - } + builder.extend(puncts.iter().map(|punct| { + tt::Leaf::from({ + let mut it = *punct; + marker(&mut it.span); + it + }) + })); } Op::Subtree { tokens, delimiter } => { let mut delimiter = *delimiter; marker(&mut delimiter.open); marker(&mut delimiter.close); - let ExpandResult { value: tt, err: e } = - expand_subtree(ctx, tokens, Some(delimiter), arena, marker); + let ExpandResult { value: (), err: e } = + expand_subtree_with_delimiter(ctx, tokens, builder, Some(delimiter), marker); err = err.or(e); - arena.push(tt.into()); } Op::Var { name, id, .. } => { - let ExpandResult { value: fragment, err: e } = expand_var(ctx, name, *id, marker); + let ExpandResult { value: (), err: e } = + expand_var(ctx, name, *id, builder, marker); err = err.or(e); - push_fragment(ctx, arena, fragment); } Op::Repeat { tokens: subtree, kind, separator } => { - let ExpandResult { value: fragment, err: e } = - expand_repeat(ctx, subtree, *kind, separator.as_deref(), arena, marker); + let ExpandResult { value: (), err: e } = + expand_repeat(ctx, subtree, *kind, separator.as_deref(), builder, marker); err = err.or(e); - push_fragment(ctx, arena, fragment) } Op::Ignore { name, id } => { // Expand the variable, but ignore the result. This registers the repetition count. // FIXME: Any emitted errors are dropped. - expand_var(ctx, name, *id, marker); + let _ = ctx.bindings.get_fragment(name, *id, &mut ctx.nesting, marker); } Op::Index { depth } => { let index = ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx); - arena.push( - tt::Leaf::Literal(tt::Literal { - symbol: Symbol::integer(index), - span: ctx.call_site, - kind: tt::LitKind::Integer, - suffix: None, - }) - .into(), - ); + builder.push(tt::Leaf::Literal(tt::Literal { + symbol: Symbol::integer(index), + span: ctx.call_site, + kind: tt::LitKind::Integer, + suffix: None, + })); } Op::Len { depth } => { let length = ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |_nest| { // FIXME: to be implemented 0 }); - arena.push( - tt::Leaf::Literal(tt::Literal { - symbol: Symbol::integer(length), - span: ctx.call_site, - kind: tt::LitKind::Integer, - suffix: None, - }) - .into(), - ); + builder.push(tt::Leaf::Literal(tt::Literal { + symbol: Symbol::integer(length), + span: ctx.call_site, + kind: tt::LitKind::Integer, + suffix: None, + })); } Op::Count { name, depth } => { let mut binding = match ctx.bindings.get(name, ctx.call_site) { @@ -302,15 +271,12 @@ fn expand_subtree( let res = count(binding, 0, depth.unwrap_or(0)); - arena.push( - tt::Leaf::Literal(tt::Literal { - symbol: Symbol::integer(res), - span: ctx.call_site, - suffix: None, - kind: tt::LitKind::Integer, - }) - .into(), - ); + builder.push(tt::Leaf::Literal(tt::Literal { + symbol: Symbol::integer(res), + span: ctx.call_site, + suffix: None, + kind: tt::LitKind::Integer, + })); } Op::Concat { elements, span: concat_span } => { let mut concatenated = String::new(); @@ -342,11 +308,22 @@ fn expand_subtree( continue; } }; - let value = match &var_value { - Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => { + let values = match &var_value { + Fragment::Tokens(tokens) => { + let mut iter = tokens.iter(); + (iter.next(), iter.next()) + } + Fragment::TokensOwned(tokens) => { + let mut iter = tokens.iter(); + (iter.next(), iter.next()) + } + _ => (None, None), + }; + let value = match values { + (Some(TtElement::Leaf(tt::Leaf::Ident(ident))), None) => { ident.sym.as_str() } - Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => { + (Some(TtElement::Leaf(tt::Leaf::Literal(lit))), None) => { lit.symbol.as_str() } _ => { @@ -382,36 +359,53 @@ fn expand_subtree( let needs_raw = parser::SyntaxKind::from_keyword(&concatenated, Edition::LATEST).is_some(); let is_raw = if needs_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No }; - arena.push(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + builder.push(tt::Leaf::Ident(tt::Ident { is_raw, span: result_span, sym: Symbol::intern(&concatenated), - }))); + })); } } } - // drain the elements added in this instance of expand_subtree - let tts = arena.drain(start_elements..).collect(); - ExpandResult { - value: tt::Subtree { - delimiter: delimiter.unwrap_or_else(|| tt::Delimiter::invisible_spanned(ctx.call_site)), - token_trees: tts, - }, - err, - } + ExpandResult { value: (), err } } fn expand_var( ctx: &mut ExpandCtx<'_>, v: &Symbol, id: Span, - marker: impl Fn(&mut Span), -) -> ExpandResult { + builder: &mut tt::TopSubtreeBuilder, + marker: impl Fn(&mut Span) + Copy, +) -> ExpandResult<()> { // We already handle $crate case in mbe parser debug_assert!(*v != sym::crate_); match ctx.bindings.get_fragment(v, id, &mut ctx.nesting, marker) { - Ok(it) => ExpandResult::ok(it), + Ok(fragment) => { + match fragment { + Fragment::Tokens(tt) => builder.extend_with_tt(tt.strip_invisible()), + Fragment::TokensOwned(tt) => builder.extend_with_tt(tt.view().strip_invisible()), + Fragment::Expr(sub) => { + let sub = sub.strip_invisible(); + let mut span = id; + marker(&mut span); + let wrap_in_parens = !matches!(sub.flat_tokens(), [tt::TokenTree::Leaf(_)]) + && sub.try_into_subtree().is_none_or(|it| { + it.top_subtree().delimiter.kind == tt::DelimiterKind::Invisible + }); + if wrap_in_parens { + builder.open(tt::DelimiterKind::Parenthesis, span); + } + builder.extend_with_tt(sub); + if wrap_in_parens { + builder.close(span); + } + } + Fragment::Path(tt) => fix_up_and_push_path_tt(ctx, builder, tt), + Fragment::Empty => (), + }; + ExpandResult::ok(()) + } Err(e) if matches!(e.inner.1, ExpandErrorKind::UnresolvedBinding(_)) => { // Note that it is possible to have a `$var` inside a macro which is not bound. // For example: @@ -426,29 +420,13 @@ fn expand_var( // } // ``` // We just treat it a normal tokens - let tt = tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(id), - token_trees: Box::new([ - tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, span: id }) - .into(), - tt::Leaf::from(tt::Ident { - sym: v.clone(), - span: id, - is_raw: tt::IdentIsRaw::No, - }) - .into(), - ]), - } - .into(); - ExpandResult::ok(Fragment::Tokens(tt)) + builder.extend([ + tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, span: id }), + tt::Leaf::from(tt::Ident { sym: v.clone(), span: id, is_raw: tt::IdentIsRaw::No }), + ]); + ExpandResult::ok(()) } - Err(e) => ExpandResult { - value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree::empty(tt::DelimSpan { - open: ctx.call_site, - close: ctx.call_site, - }))), - err: Some(e), - }, + Err(e) => ExpandResult::only_err(e), } } @@ -457,21 +435,20 @@ fn expand_repeat( template: &MetaTemplate, kind: RepeatKind, separator: Option<&Separator>, - arena: &mut Vec>, + builder: &mut tt::TopSubtreeBuilder, marker: impl Fn(&mut Span) + Copy, -) -> ExpandResult { - let mut buf: Vec> = Vec::new(); +) -> ExpandResult<()> { ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false }); // Dirty hack to make macro-expansion terminate. // This should be replaced by a proper macro-by-example implementation let limit = 65536; - let mut has_seps = 0; let mut counter = 0; let mut err = None; + let mut restore_point = builder.restore_point(); loop { - let ExpandResult { value: mut t, err: e } = - expand_subtree(ctx, template, None, arena, marker); + let ExpandResult { value: (), err: e } = + expand_subtree_with_delimiter(ctx, template, builder, None, marker); let nesting_state = ctx.nesting.last_mut().unwrap(); if nesting_state.at_end || !nesting_state.hit { break; @@ -479,23 +456,14 @@ fn expand_repeat( nesting_state.idx += 1; nesting_state.hit = false; + builder.remove_last_subtree_if_invisible(); + + restore_point = builder.restore_point(); + counter += 1; if counter == limit { - tracing::warn!( - "expand_tt in repeat pattern exceed limit => {:#?}\n{:#?}", - template, - ctx - ); - return ExpandResult { - value: Fragment::Tokens( - tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(ctx.call_site), - token_trees: Box::new([]), - } - .into(), - ), - err: Some(ExpandError::new(ctx.call_site, ExpandErrorKind::LimitExceeded)), - }; + err = Some(ExpandError::new(ctx.call_site, ExpandErrorKind::LimitExceeded)); + break; } if e.is_some() { @@ -503,24 +471,14 @@ fn expand_repeat( continue; } - t.delimiter.kind = tt::DelimiterKind::Invisible; - push_subtree(&mut buf, t); - if let Some(sep) = separator { - has_seps = match sep { - Separator::Ident(ident) => { - buf.push(tt::Leaf::from(ident.clone()).into()); - 1 - } - Separator::Literal(lit) => { - buf.push(tt::Leaf::from(lit.clone()).into()); - 1 - } + match sep { + Separator::Ident(ident) => builder.push(tt::Leaf::from(ident.clone())), + Separator::Literal(lit) => builder.push(tt::Leaf::from(lit.clone())), Separator::Puncts(puncts) => { for &punct in puncts { - buf.push(tt::Leaf::from(punct).into()); + builder.push(tt::Leaf::from(punct)); } - puncts.len() } }; } @@ -529,46 +487,18 @@ fn expand_repeat( break; } } + // Lose the last separator and last after-the-end round. + builder.restore(restore_point); ctx.nesting.pop().unwrap(); - for _ in 0..has_seps { - buf.pop(); - } // Check if it is a single token subtree without any delimiter // e.g {Delimiter:None> ['>'] /Delimiter:None>} - let tt = tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(ctx.call_site), - token_trees: buf.into_boxed_slice(), - }; - if RepeatKind::OneOrMore == kind && counter == 0 { - let span = tt.delimiter.open; - return ExpandResult { - value: Fragment::Tokens(tt.into()), - err: Some(ExpandError::new(span, ExpandErrorKind::UnexpectedToken)), - }; - } - ExpandResult { value: Fragment::Tokens(tt.into()), err } -} - -fn push_fragment(ctx: &ExpandCtx<'_>, buf: &mut Vec>, fragment: Fragment) { - match fragment { - Fragment::Tokens(tt::TokenTree::Subtree(tt)) => push_subtree(buf, tt), - Fragment::Expr(sub) => { - push_subtree(buf, sub); - } - Fragment::Path(tt) => fix_up_and_push_path_tt(ctx, buf, tt), - Fragment::Tokens(tt) => buf.push(tt), - Fragment::Empty => (), - } -} - -fn push_subtree(buf: &mut Vec>, tt: tt::Subtree) { - match tt.delimiter.kind { - tt::DelimiterKind::Invisible => buf.extend(Vec::from(tt.token_trees)), - _ => buf.push(tt.into()), + if RepeatKind::OneOrMore == kind && counter == 0 && err.is_none() { + err = Some(ExpandError::new(ctx.call_site, ExpandErrorKind::UnexpectedToken)); } + ExpandResult { value: (), err } } /// Inserts the path separator `::` between an identifier and its following generic @@ -576,47 +506,45 @@ fn push_subtree(buf: &mut Vec>, tt: tt::Subtree) { /// we need this fixup. fn fix_up_and_push_path_tt( ctx: &ExpandCtx<'_>, - buf: &mut Vec>, - subtree: tt::Subtree, + builder: &mut tt::TopSubtreeBuilder, + subtree: tt::TokenTreesView<'_, Span>, ) { - stdx::always!(matches!(subtree.delimiter.kind, tt::DelimiterKind::Invisible)); let mut prev_was_ident = false; // Note that we only need to fix up the top-level `TokenTree`s because the // context of the paths in the descendant `Subtree`s won't be changed by the // mbe transcription. - for tt in Vec::from(subtree.token_trees) { + let mut iter = subtree.iter(); + while let Some(tt) = iter.next_as_view() { if prev_was_ident { // Pedantically, `(T) -> U` in `FnOnce(T) -> U` is treated as a generic // argument list and thus needs `::` between it and `FnOnce`. However in // today's Rust this type of path *semantically* cannot appear as a // top-level expression-context path, so we can safely ignore it. - if let tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '<', .. })) = tt { - buf.push( + if let [tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '<', .. }))] = + tt.flat_tokens() + { + builder.extend([ tt::Leaf::Punct(tt::Punct { char: ':', spacing: tt::Spacing::Joint, span: ctx.call_site, - }) - .into(), - ); - buf.push( + }), tt::Leaf::Punct(tt::Punct { char: ':', spacing: tt::Spacing::Alone, span: ctx.call_site, - }) - .into(), - ); + }), + ]); } } - prev_was_ident = matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(_))); - buf.push(tt); + prev_was_ident = matches!(tt.flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(_))]); + builder.extend_with_tt(tt); } } /// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth /// defined by the metavar expression. -fn count(binding: &Binding, depth_curr: usize, depth_max: usize) -> usize { +fn count(binding: &Binding<'_>, depth_curr: usize, depth_max: usize) -> usize { match binding { Binding::Nested(bs) => { if depth_curr == depth_max { diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index ca10a2be2732..6abf56d4b37c 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -148,17 +148,17 @@ impl DeclarativeMacro { /// The old, `macro_rules! m {}` flavor. pub fn parse_macro_rules( - tt: &tt::Subtree, + tt: &tt::TopSubtree, ctx_edition: impl Copy + Fn(SyntaxContextId) -> Edition, ) -> DeclarativeMacro { // Note: this parsing can be implemented using mbe machinery itself, by // matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing // manually seems easier. - let mut src = TtIter::new(tt); + let mut src = tt.iter(); let mut rules = Vec::new(); let mut err = None; - while src.len() > 0 { + while !src.is_empty() { let rule = match Rule::parse(ctx_edition, &mut src) { Ok(it) => it, Err(e) => { @@ -168,7 +168,7 @@ impl DeclarativeMacro { }; rules.push(rule); if let Err(()) = src.expect_char(';') { - if src.len() > 0 { + if !src.is_empty() { err = Some(Box::new(ParseError::expected("expected `;`"))); } break; @@ -187,8 +187,8 @@ impl DeclarativeMacro { /// The new, unstable `macro m {}` flavor. pub fn parse_macro2( - args: Option<&tt::Subtree>, - body: &tt::Subtree, + args: Option<&tt::TopSubtree>, + body: &tt::TopSubtree, ctx_edition: impl Copy + Fn(SyntaxContextId) -> Edition, ) -> DeclarativeMacro { let mut rules = Vec::new(); @@ -198,8 +198,8 @@ impl DeclarativeMacro { cov_mark::hit!(parse_macro_def_simple); let rule = (|| { - let lhs = MetaTemplate::parse_pattern(ctx_edition, args)?; - let rhs = MetaTemplate::parse_template(ctx_edition, body)?; + let lhs = MetaTemplate::parse_pattern(ctx_edition, args.iter())?; + let rhs = MetaTemplate::parse_template(ctx_edition, body.iter())?; Ok(crate::Rule { lhs, rhs }) })(); @@ -210,8 +210,8 @@ impl DeclarativeMacro { } } else { cov_mark::hit!(parse_macro_def_rules); - let mut src = TtIter::new(body); - while src.len() > 0 { + let mut src = body.iter(); + while !src.is_empty() { let rule = match Rule::parse(ctx_edition, &mut src) { Ok(it) => it, Err(e) => { @@ -221,7 +221,7 @@ impl DeclarativeMacro { }; rules.push(rule); if let Err(()) = src.expect_any_char(&[';', ',']) { - if src.len() > 0 { + if !src.is_empty() { err = Some(Box::new(ParseError::expected( "expected `;` or `,` to delimit rules", ))); @@ -251,11 +251,11 @@ impl DeclarativeMacro { pub fn expand( &self, - tt: &tt::Subtree, + tt: &tt::TopSubtree, marker: impl Fn(&mut Span) + Copy, call_site: Span, def_site_edition: Edition, - ) -> ExpandResult<(tt::Subtree, MatchedArmIndex)> { + ) -> ExpandResult<(tt::TopSubtree, MatchedArmIndex)> { expander::expand_rules(&self.rules, tt, marker, call_site, def_site_edition) } } @@ -265,10 +265,12 @@ impl Rule { edition: impl Copy + Fn(SyntaxContextId) -> Edition, src: &mut TtIter<'_, Span>, ) -> Result { - let lhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; + let (_, lhs) = + src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; src.expect_char('=').map_err(|()| ParseError::expected("expected `=`"))?; src.expect_char('>').map_err(|()| ParseError::expected("expected `>`"))?; - let rhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; + let (_, rhs) = + src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; let lhs = MetaTemplate::parse_pattern(edition, lhs)?; let rhs = MetaTemplate::parse_template(edition, rhs)?; @@ -359,17 +361,17 @@ impl From> for ValueResult { } } -pub fn expect_fragment( - tt_iter: &mut TtIter<'_, Span>, +pub fn expect_fragment<'t>( + tt_iter: &mut TtIter<'t, Span>, entry_point: ::parser::PrefixEntryPoint, edition: ::parser::Edition, delim_span: DelimSpan, -) -> ExpandResult>> { +) -> ExpandResult> { use ::parser; - let buffer = tt::buffer::TokenBuffer::from_tokens(tt_iter.as_slice()); - let parser_input = to_parser_input(edition, &buffer); + let buffer = tt_iter.remaining(); + let parser_input = to_parser_input(edition, buffer); let tree_traversal = entry_point.parse(&parser_input, edition); - let mut cursor = buffer.begin(); + let mut cursor = buffer.cursor(); let mut error = false; for step in tree_traversal.iter() { match step { @@ -378,13 +380,13 @@ pub fn expect_fragment( n_input_tokens = 2; } for _ in 0..n_input_tokens { - cursor = cursor.bump_subtree(); + cursor.bump_or_end(); } } parser::Step::FloatSplit { .. } => { // FIXME: We need to split the tree properly here, but mutating the token trees // in the buffer is somewhat tricky to pull off. - cursor = cursor.bump_subtree(); + cursor.bump_or_end(); } parser::Step::Enter { .. } | parser::Step::Exit => (), parser::Step::Error { .. } => error = true, @@ -393,29 +395,19 @@ pub fn expect_fragment( let err = if error || !cursor.is_root() { Some(ExpandError::binding_error( - buffer.begin().token_tree().map_or(delim_span.close, |tt| tt.span()), + buffer.cursor().token_tree().map_or(delim_span.close, |tt| tt.first_span()), format!("expected {entry_point:?}"), )) } else { None }; - let mut curr = buffer.begin(); - let mut res = vec![]; - - while curr != cursor { - let Some(token) = curr.token_tree() else { break }; - res.push(token.cloned()); - curr = curr.bump(); + while !cursor.is_root() { + cursor.bump_or_end(); } - *tt_iter = TtIter::new_iter(tt_iter.as_slice()[res.len()..].iter()); - let res = match &*res { - [] | [_] => res.pop(), - [first, ..] => Some(tt::TokenTree::Subtree(tt::Subtree { - delimiter: Delimiter::invisible_spanned(first.first_span()), - token_trees: res.into_boxed_slice(), - })), - }; + let res = cursor.crossed(); + tt_iter.flat_advance(res.len()); + ExpandResult { value: res, err } } diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs index b55edf4a5e0c..16d55492a04b 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use arrayvec::ArrayVec; use intern::{sym, Symbol}; use span::{Edition, Span, SyntaxContextId}; -use tt::iter::TtIter; +use tt::iter::{TtElement, TtIter}; use crate::ParseError; @@ -29,14 +29,14 @@ pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>); impl MetaTemplate { pub(crate) fn parse_pattern( edition: impl Copy + Fn(SyntaxContextId) -> Edition, - pattern: &tt::Subtree, + pattern: TtIter<'_, Span>, ) -> Result { MetaTemplate::parse(edition, pattern, Mode::Pattern) } pub(crate) fn parse_template( edition: impl Copy + Fn(SyntaxContextId) -> Edition, - template: &tt::Subtree, + template: TtIter<'_, Span>, ) -> Result { MetaTemplate::parse(edition, template, Mode::Template) } @@ -47,13 +47,11 @@ impl MetaTemplate { fn parse( edition: impl Copy + Fn(SyntaxContextId) -> Edition, - tt: &tt::Subtree, + mut src: TtIter<'_, Span>, mode: Mode, ) -> Result { - let mut src = TtIter::new(tt); - let mut res = Vec::new(); - while let Some(first) = src.peek_n(0) { + while let Some(first) = src.peek() { let op = next_op(edition, first, &mut src, mode)?; res.push(op); } @@ -182,12 +180,12 @@ enum Mode { fn next_op( edition: impl Copy + Fn(SyntaxContextId) -> Edition, - first_peeked: &tt::TokenTree, + first_peeked: TtElement<'_, Span>, src: &mut TtIter<'_, Span>, mode: Mode, ) -> Result { let res = match first_peeked { - tt::TokenTree::Leaf(tt::Leaf::Punct(p @ tt::Punct { char: '$', .. })) => { + TtElement::Leaf(tt::Leaf::Punct(p @ tt::Punct { char: '$', .. })) => { src.next().expect("first token already peeked"); // Note that the '$' itself is a valid token inside macro_rules. let second = match src.next() { @@ -201,18 +199,16 @@ fn next_op( Some(it) => it, }; match second { - tt::TokenTree::Subtree(subtree) => match subtree.delimiter.kind { + TtElement::Subtree(subtree, mut subtree_iter) => match subtree.delimiter.kind { tt::DelimiterKind::Parenthesis => { let (separator, kind) = parse_repeat(src)?; - let tokens = MetaTemplate::parse(edition, subtree, mode)?; + let tokens = MetaTemplate::parse(edition, subtree_iter, mode)?; Op::Repeat { tokens, separator: separator.map(Arc::new), kind } } tt::DelimiterKind::Brace => match mode { - Mode::Template => { - parse_metavar_expr(&mut TtIter::new(subtree)).map_err(|()| { - ParseError::unexpected("invalid metavariable expression") - })? - } + Mode::Template => parse_metavar_expr(&mut subtree_iter).map_err(|()| { + ParseError::unexpected("invalid metavariable expression") + })?, Mode::Pattern => { return Err(ParseError::unexpected( "`${}` metavariable expressions are not allowed in matchers", @@ -225,7 +221,7 @@ fn next_op( )) } }, - tt::TokenTree::Leaf(leaf) => match leaf { + TtElement::Leaf(leaf) => match leaf { tt::Leaf::Ident(ident) if ident.sym == sym::crate_ => { // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. Op::Ident(tt::Ident { @@ -265,25 +261,25 @@ fn next_op( } } - tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => { + TtElement::Leaf(tt::Leaf::Literal(it)) => { src.next().expect("first token already peeked"); Op::Literal(it.clone()) } - tt::TokenTree::Leaf(tt::Leaf::Ident(it)) => { + TtElement::Leaf(tt::Leaf::Ident(it)) => { src.next().expect("first token already peeked"); Op::Ident(it.clone()) } - tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => { + TtElement::Leaf(tt::Leaf::Punct(_)) => { // There's at least one punct so this shouldn't fail. let puncts = src.expect_glued_punct().unwrap(); Op::Punct(Box::new(puncts)) } - tt::TokenTree::Subtree(subtree) => { + TtElement::Subtree(subtree, subtree_iter) => { src.next().expect("first token already peeked"); - let tokens = MetaTemplate::parse(edition, subtree, mode)?; + let tokens = MetaTemplate::parse(edition, subtree_iter, mode)?; Op::Subtree { tokens, delimiter: subtree.delimiter } } }; @@ -343,8 +339,8 @@ fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option, Repeat let mut separator = Separator::Puncts(ArrayVec::new()); for tt in src { let tt = match tt { - tt::TokenTree::Leaf(leaf) => leaf, - tt::TokenTree::Subtree(_) => return Err(ParseError::InvalidRepeat), + TtElement::Leaf(leaf) => leaf, + TtElement::Subtree(..) => return Err(ParseError::InvalidRepeat), }; let has_sep = match &separator { Separator::Puncts(puncts) => !puncts.is_empty(), @@ -378,37 +374,39 @@ fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option, Repeat fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result { let func = src.expect_ident()?; - let args = src.expect_subtree()?; + let (args, mut args_iter) = src.expect_subtree()?; if args.delimiter.kind != tt::DelimiterKind::Parenthesis { return Err(()); } - let mut args = TtIter::new(args); - let op = match &func.sym { s if sym::ignore == *s => { - args.expect_dollar()?; - let ident = args.expect_ident()?; + args_iter.expect_dollar()?; + let ident = args_iter.expect_ident()?; Op::Ignore { name: ident.sym.clone(), id: ident.span } } - s if sym::index == *s => Op::Index { depth: parse_depth(&mut args)? }, - s if sym::len == *s => Op::Len { depth: parse_depth(&mut args)? }, + s if sym::index == *s => Op::Index { depth: parse_depth(&mut args_iter)? }, + s if sym::len == *s => Op::Len { depth: parse_depth(&mut args_iter)? }, s if sym::count == *s => { - args.expect_dollar()?; - let ident = args.expect_ident()?; - let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None }; + args_iter.expect_dollar()?; + let ident = args_iter.expect_ident()?; + let depth = if try_eat_comma(&mut args_iter) { + Some(parse_depth(&mut args_iter)?) + } else { + None + }; Op::Count { name: ident.sym.clone(), depth } } s if sym::concat == *s => { let mut elements = Vec::new(); - while let Some(next) = args.peek_n(0) { - let element = if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = next { - args.next().expect("already peeked"); + while let Some(next) = args_iter.peek() { + let element = if let TtElement::Leaf(tt::Leaf::Literal(lit)) = next { + args_iter.next().expect("already peeked"); ConcatMetaVarExprElem::Literal(lit.clone()) } else { - let is_var = try_eat_dollar(&mut args); - let ident = args.expect_ident_or_underscore()?.clone(); + let is_var = try_eat_dollar(&mut args_iter); + let ident = args_iter.expect_ident_or_underscore()?.clone(); if is_var { ConcatMetaVarExprElem::Var(ident) @@ -417,8 +415,8 @@ fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result { } }; elements.push(element); - if args.peek_n(0).is_some() { - args.expect_comma()?; + if !args_iter.is_empty() { + args_iter.expect_comma()?; } } if elements.len() < 2 { @@ -429,7 +427,7 @@ fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result { _ => return Err(()), }; - if args.next().is_some() { + if args_iter.next().is_some() { return Err(()); } @@ -437,7 +435,7 @@ fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result { } fn parse_depth(src: &mut TtIter<'_, Span>) -> Result { - if src.len() == 0 { + if src.is_empty() { Ok(0) } else if let tt::Leaf::Literal(tt::Literal { symbol: text, suffix: None, .. }) = src.expect_literal()? @@ -450,7 +448,7 @@ fn parse_depth(src: &mut TtIter<'_, Span>) -> Result { } fn try_eat_comma(src: &mut TtIter<'_, Span>) -> bool { - if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) = src.peek_n(0) { + if let Some(TtElement::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) = src.peek() { let _ = src.next(); return true; } @@ -458,7 +456,7 @@ fn try_eat_comma(src: &mut TtIter<'_, Span>) -> bool { } fn try_eat_dollar(src: &mut TtIter<'_, Span>) -> bool { - if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. }))) = src.peek_n(0) { + if let Some(TtElement::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. }))) = src.peek() { let _ = src.next(); return true; } diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs deleted file mode 100644 index 2988fb3cf154..000000000000 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs +++ /dev/null @@ -1,101 +0,0 @@ -use rustc_hash::FxHashMap; -use span::Span; -use syntax::{ast, AstNode}; -use test_utils::extract_annotations; -use tt::{ - buffer::{TokenBuffer, TokenTreeRef}, - Leaf, Punct, Spacing, -}; - -use crate::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY}; - -fn check_punct_spacing(fixture: &str) { - let source_file = ast::SourceFile::parse(fixture, span::Edition::CURRENT).ok().unwrap(); - let subtree = syntax_node_to_token_tree( - source_file.syntax(), - DummyTestSpanMap, - DUMMY, - DocCommentDesugarMode::Mbe, - ); - let mut annotations: FxHashMap<_, _> = extract_annotations(fixture) - .into_iter() - .map(|(range, annotation)| { - let spacing = match annotation.as_str() { - "Alone" => Spacing::Alone, - "Joint" => Spacing::Joint, - a => panic!("unknown annotation: {a}"), - }; - (range, spacing) - }) - .collect(); - - let buf = TokenBuffer::from_subtree(&subtree); - let mut cursor = buf.begin(); - while !cursor.eof() { - while let Some(token_tree) = cursor.token_tree() { - if let TokenTreeRef::Leaf( - Leaf::Punct(Punct { spacing, span: Span { range, .. }, .. }), - _, - ) = token_tree - { - if let Some(expected) = annotations.remove(range) { - assert_eq!(expected, *spacing); - } - } - cursor = cursor.bump_subtree(); - } - cursor = cursor.bump(); - } - - assert!(annotations.is_empty(), "unchecked annotations: {annotations:?}"); -} - -#[test] -fn punct_spacing() { - check_punct_spacing( - r#" -fn main() { - 0+0; - //^ Alone - 0+(0); - //^ Alone - 0<=0; - //^ Joint - // ^ Alone - 0<=(0); - // ^ Alone - a=0; - //^ Alone - a=(0); - //^ Alone - a+=0; - //^ Joint - // ^ Alone - a+=(0); - // ^ Alone - a&&b; - //^ Joint - // ^ Alone - a&&(b); - // ^ Alone - foo::bar; - // ^ Joint - // ^ Alone - use foo::{bar,baz,}; - // ^ Alone - // ^ Alone - // ^ Alone - struct Struct<'a> {}; - // ^ Joint - // ^ Joint - Struct::<0>; - // ^ Alone - Struct::<{0}>; - // ^ Alone - ;; - //^ Joint - // ^ Alone -} - "#, - ); -} diff --git a/src/tools/rust-analyzer/crates/parser/src/input.rs b/src/tools/rust-analyzer/crates/parser/src/input.rs index 9504bd4d9ec8..c90b358cfbb4 100644 --- a/src/tools/rust-analyzer/crates/parser/src/input.rs +++ b/src/tools/rust-analyzer/crates/parser/src/input.rs @@ -72,7 +72,7 @@ impl Input { } pub(crate) fn is_joint(&self, n: usize) -> bool { let (idx, b_idx) = self.bit_index(n); - self.joint[idx] & 1 << b_idx != 0 + self.joint[idx] & (1 << b_idx) != 0 } } diff --git a/src/tools/rust-analyzer/crates/parser/src/output.rs b/src/tools/rust-analyzer/crates/parser/src/output.rs index 41d4c68b2d74..386d03a62cc1 100644 --- a/src/tools/rust-analyzer/crates/parser/src/output.rs +++ b/src/tools/rust-analyzer/crates/parser/src/output.rs @@ -85,7 +85,7 @@ impl Output { } pub(crate) fn float_split_hack(&mut self, ends_in_dot: bool) { - let e = (Self::SPLIT_EVENT as u32) << Self::TAG_SHIFT + let e = ((Self::SPLIT_EVENT as u32) << Self::TAG_SHIFT) | ((ends_in_dot as u32) << Self::N_INPUT_TOKEN_SHIFT) | Self::EVENT_MASK; self.event.push(e); @@ -99,7 +99,7 @@ impl Output { } pub(crate) fn leave_node(&mut self) { - let e = (Self::EXIT_EVENT as u32) << Self::TAG_SHIFT | Self::EVENT_MASK; + let e = ((Self::EXIT_EVENT as u32) << Self::TAG_SHIFT) | Self::EVENT_MASK; self.event.push(e) } diff --git a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs index 7adedba7c438..32569d5c3fe9 100644 --- a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs +++ b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs @@ -5,7 +5,7 @@ //! abstract token parsing, and string tokenization as completely separate //! layers. //! -//! However, often you do pares text into syntax trees and the glue code for +//! However, often you do parse text into syntax trees and the glue code for //! that needs to live somewhere. Rather than putting it to lexer or parser, we //! use a separate shortcuts module for that. 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 0c9c6ffd715e..318f71a2d4df 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 @@ -331,6 +331,331 @@ pub enum SyntaxKind { } use self::SyntaxKind::*; impl SyntaxKind { + #[allow(unreachable_patterns)] + pub const fn text(self) -> &'static str { + match self { + TOMBSTONE + | EOF + | __LAST + | BYTE + | BYTE_STRING + | CHAR + | C_STRING + | FLOAT_NUMBER + | INT_NUMBER + | RAW_BYTE_STRING + | RAW_C_STRING + | RAW_STRING + | STRING + | ABI + | ADT + | ARG_LIST + | ARRAY_EXPR + | ARRAY_TYPE + | ASM_CLOBBER_ABI + | ASM_CONST + | ASM_DIR_SPEC + | ASM_EXPR + | ASM_LABEL + | ASM_OPERAND + | ASM_OPERAND_EXPR + | ASM_OPERAND_NAMED + | ASM_OPTION + | ASM_OPTIONS + | ASM_PIECE + | ASM_REG_OPERAND + | ASM_REG_SPEC + | ASM_SYM + | ASSOC_ITEM + | ASSOC_ITEM_LIST + | ASSOC_TYPE_ARG + | ATTR + | AWAIT_EXPR + | BECOME_EXPR + | BIN_EXPR + | BLOCK_EXPR + | BOX_PAT + | BREAK_EXPR + | CALL_EXPR + | CAST_EXPR + | CLOSURE_BINDER + | CLOSURE_EXPR + | CONST + | CONST_ARG + | CONST_BLOCK_PAT + | CONST_PARAM + | CONTINUE_EXPR + | DYN_TRAIT_TYPE + | ENUM + | EXPR + | EXPR_STMT + | EXTERN_BLOCK + | EXTERN_CRATE + | EXTERN_ITEM + | EXTERN_ITEM_LIST + | FIELD_EXPR + | FIELD_LIST + | FN + | FN_PTR_TYPE + | FORMAT_ARGS_ARG + | FORMAT_ARGS_EXPR + | FOR_EXPR + | FOR_TYPE + | GENERIC_ARG + | GENERIC_ARG_LIST + | GENERIC_PARAM + | GENERIC_PARAM_LIST + | IDENT_PAT + | IF_EXPR + | IMPL + | IMPL_TRAIT_TYPE + | INDEX_EXPR + | INFER_TYPE + | ITEM + | ITEM_LIST + | LABEL + | LET_ELSE + | LET_EXPR + | LET_STMT + | LIFETIME + | LIFETIME_ARG + | LIFETIME_PARAM + | LITERAL + | LITERAL_PAT + | LOOP_EXPR + | MACRO_CALL + | MACRO_DEF + | MACRO_EXPR + | MACRO_ITEMS + | MACRO_PAT + | MACRO_RULES + | MACRO_STMTS + | MACRO_TYPE + | MATCH_ARM + | MATCH_ARM_LIST + | MATCH_EXPR + | MATCH_GUARD + | META + | METHOD_CALL_EXPR + | MODULE + | NAME + | NAME_REF + | NEVER_TYPE + | OFFSET_OF_EXPR + | OR_PAT + | PARAM + | PARAM_LIST + | PARENTHESIZED_ARG_LIST + | PAREN_EXPR + | PAREN_PAT + | PAREN_TYPE + | PAT + | PATH + | PATH_EXPR + | PATH_PAT + | PATH_SEGMENT + | PATH_TYPE + | PREFIX_EXPR + | PTR_TYPE + | RANGE_EXPR + | RANGE_PAT + | RECORD_EXPR + | RECORD_EXPR_FIELD + | RECORD_EXPR_FIELD_LIST + | RECORD_FIELD + | RECORD_FIELD_LIST + | RECORD_PAT + | RECORD_PAT_FIELD + | RECORD_PAT_FIELD_LIST + | REF_EXPR + | REF_PAT + | REF_TYPE + | RENAME + | REST_PAT + | RETURN_EXPR + | RETURN_TYPE_SYNTAX + | RET_TYPE + | SELF_PARAM + | SLICE_PAT + | SLICE_TYPE + | SOURCE_FILE + | STATIC + | STMT + | STMT_LIST + | STRUCT + | TOKEN_TREE + | TRAIT + | TRAIT_ALIAS + | TRY_EXPR + | TUPLE_EXPR + | TUPLE_FIELD + | TUPLE_FIELD_LIST + | TUPLE_PAT + | TUPLE_STRUCT_PAT + | TUPLE_TYPE + | TYPE + | TYPE_ALIAS + | TYPE_ARG + | TYPE_BOUND + | TYPE_BOUND_LIST + | TYPE_PARAM + | UNDERSCORE_EXPR + | UNION + | USE + | USE_BOUND_GENERIC_ARG + | USE_BOUND_GENERIC_ARGS + | USE_TREE + | USE_TREE_LIST + | VARIANT + | VARIANT_LIST + | VISIBILITY + | WHERE_CLAUSE + | WHERE_PRED + | WHILE_EXPR + | WILDCARD_PAT + | YEET_EXPR + | YIELD_EXPR + | COMMENT + | ERROR + | IDENT + | LIFETIME_IDENT + | NEWLINE + | SHEBANG + | WHITESPACE => panic!("no text for these `SyntaxKind`s"), + DOLLAR => "$", + SEMICOLON => ";", + COMMA => ",", + L_PAREN => "(", + R_PAREN => ")", + L_CURLY => "{", + R_CURLY => "}", + L_BRACK => "[", + R_BRACK => "]", + L_ANGLE => "<", + R_ANGLE => ">", + AT => "@", + POUND => "#", + TILDE => "~", + QUESTION => "?", + AMP => "&", + PIPE => "|", + PLUS => "+", + STAR => "*", + SLASH => "/", + CARET => "^", + PERCENT => "%", + UNDERSCORE => "_", + DOT => ".", + DOT2 => "..", + DOT3 => "...", + DOT2EQ => "..=", + COLON => ":", + COLON2 => "::", + EQ => "=", + EQ2 => "==", + FAT_ARROW => "=>", + BANG => "!", + NEQ => "!=", + MINUS => "-", + THIN_ARROW => "->", + LTEQ => "<=", + GTEQ => ">=", + PLUSEQ => "+=", + MINUSEQ => "-=", + PIPEEQ => "|=", + AMPEQ => "&=", + CARETEQ => "^=", + SLASHEQ => "/=", + STAREQ => "*=", + PERCENTEQ => "%=", + AMP2 => "&&", + PIPE2 => "||", + SHL => "<<", + SHR => ">>", + SHLEQ => "<<=", + SHREQ => ">>=", + SELF_TYPE_KW => "Self", + ABSTRACT_KW => "abstract", + AS_KW => "as", + BECOME_KW => "become", + BOX_KW => "box", + BREAK_KW => "break", + CONST_KW => "const", + CONTINUE_KW => "continue", + CRATE_KW => "crate", + DO_KW => "do", + ELSE_KW => "else", + ENUM_KW => "enum", + EXTERN_KW => "extern", + FALSE_KW => "false", + FINAL_KW => "final", + FN_KW => "fn", + FOR_KW => "for", + IF_KW => "if", + IMPL_KW => "impl", + IN_KW => "in", + LET_KW => "let", + LOOP_KW => "loop", + MACRO_KW => "macro", + MATCH_KW => "match", + MOD_KW => "mod", + MOVE_KW => "move", + MUT_KW => "mut", + OVERRIDE_KW => "override", + PRIV_KW => "priv", + PUB_KW => "pub", + REF_KW => "ref", + RETURN_KW => "return", + SELF_KW => "self", + STATIC_KW => "static", + STRUCT_KW => "struct", + SUPER_KW => "super", + TRAIT_KW => "trait", + TRUE_KW => "true", + TYPE_KW => "type", + TYPEOF_KW => "typeof", + UNSAFE_KW => "unsafe", + UNSIZED_KW => "unsized", + USE_KW => "use", + VIRTUAL_KW => "virtual", + WHERE_KW => "where", + WHILE_KW => "while", + YIELD_KW => "yield", + ASM_KW => "asm", + ATT_SYNTAX_KW => "att_syntax", + AUTO_KW => "auto", + BUILTIN_KW => "builtin", + CLOBBER_ABI_KW => "clobber_abi", + DEFAULT_KW => "default", + DYN_KW => "dyn", + FORMAT_ARGS_KW => "format_args", + INLATEOUT_KW => "inlateout", + INOUT_KW => "inout", + LABEL_KW => "label", + LATEOUT_KW => "lateout", + MACRO_RULES_KW => "macro_rules", + MAY_UNWIND_KW => "may_unwind", + NOMEM_KW => "nomem", + NORETURN_KW => "noreturn", + NOSTACK_KW => "nostack", + OFFSET_OF_KW => "offset_of", + OPTIONS_KW => "options", + OUT_KW => "out", + PRESERVES_FLAGS_KW => "preserves_flags", + PURE_KW => "pure", + RAW_KW => "raw", + READONLY_KW => "readonly", + SAFE_KW => "safe", + SYM_KW => "sym", + UNION_KW => "union", + YEET_KW => "yeet", + ASYNC_KW => "async", + AWAIT_KW => "await", + DYN_KW => "dyn", + GEN_KW => "gen", + TRY_KW => "try", + } + } #[doc = r" Checks whether this syntax kind is a strict keyword for the given edition."] #[doc = r" Strict keywords are identifiers that are always considered keywords."] pub fn is_strict_keyword(self, edition: Edition) -> bool { diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/json.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/json.rs similarity index 100% rename from src/tools/rust-analyzer/crates/proc-macro-api/src/json.rs rename to src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/json.rs diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs similarity index 66% rename from src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs rename to src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs index bbd9f582df9a..6ea8db9a9058 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -9,10 +9,10 @@ use serde_derive::{Deserialize, Serialize}; use crate::ProcMacroKind; -pub use crate::msg::flat::{ +pub use self::flat::{ deserialize_span_data_index_map, serialize_span_data_index_map, FlatTree, SpanDataIndexMap, - TokenId, }; +pub use span::TokenId; // The versions of the server protocol pub const NO_VERSION_CHECK_VERSION: u32 = 0; @@ -160,11 +160,14 @@ type ProtocolWrite = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str) mod tests { use intern::{sym, Symbol}; use span::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TextSize}; - use tt::{Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, Subtree, TokenTree}; + use tt::{ + Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree, + TopSubtreeBuilder, + }; use super::*; - fn fixture_token_tree() -> Subtree { + fn fixture_token_tree() -> TopSubtree { let anchor = SpanAnchor { file_id: span::EditionedFileId::new( span::FileId::from_raw(0xe4e4e), @@ -173,93 +176,88 @@ mod tests { ast_id: ErasedFileAstId::from_raw(0), }; - let token_trees = Box::new([ - TokenTree::Leaf( - Ident { - sym: Symbol::intern("struct"), - span: Span { - range: TextRange::at(TextSize::new(0), TextSize::of("struct")), - anchor, - ctx: SyntaxContextId::ROOT, - }, - is_raw: tt::IdentIsRaw::No, - } - .into(), - ), - TokenTree::Leaf( - Ident { - sym: Symbol::intern("Foo"), - span: Span { - range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")), - anchor, - ctx: SyntaxContextId::ROOT, - }, - is_raw: tt::IdentIsRaw::Yes, - } - .into(), - ), - TokenTree::Leaf(Leaf::Literal(Literal { - symbol: Symbol::intern("Foo"), - span: Span { - range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")), - anchor, - ctx: SyntaxContextId::ROOT, - }, - kind: tt::LitKind::Str, - suffix: None, - })), - TokenTree::Leaf(Leaf::Punct(Punct { - char: '@', - span: Span { - range: TextRange::at(TextSize::new(13), TextSize::of('@')), - anchor, - ctx: SyntaxContextId::ROOT, - }, - spacing: Spacing::Joint, - })), - TokenTree::Subtree(Subtree { - delimiter: Delimiter { - open: Span { - range: TextRange::at(TextSize::new(14), TextSize::of('{')), - anchor, - ctx: SyntaxContextId::ROOT, - }, - close: Span { - range: TextRange::at(TextSize::new(19), TextSize::of('}')), - anchor, - ctx: SyntaxContextId::ROOT, - }, - kind: DelimiterKind::Brace, - }, - token_trees: Box::new([TokenTree::Leaf(Leaf::Literal(Literal { - symbol: sym::INTEGER_0.clone(), - span: Span { - range: TextRange::at(TextSize::new(15), TextSize::of("0u32")), - anchor, - ctx: SyntaxContextId::ROOT, - }, - kind: tt::LitKind::Integer, - suffix: Some(sym::u32.clone()), - }))]), - }), - ]); - - Subtree { - delimiter: Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContextId::ROOT, - }, - close: Span { - range: TextRange::empty(TextSize::new(19)), - anchor, - ctx: SyntaxContextId::ROOT, - }, - kind: DelimiterKind::Invisible, + let mut builder = TopSubtreeBuilder::new(Delimiter { + open: Span { + range: TextRange::empty(TextSize::new(0)), + anchor, + ctx: SyntaxContextId::ROOT, }, - token_trees, - } + close: Span { + range: TextRange::empty(TextSize::new(19)), + anchor, + ctx: SyntaxContextId::ROOT, + }, + kind: DelimiterKind::Invisible, + }); + + builder.push( + Ident { + sym: Symbol::intern("struct"), + span: Span { + range: TextRange::at(TextSize::new(0), TextSize::of("struct")), + anchor, + ctx: SyntaxContextId::ROOT, + }, + is_raw: tt::IdentIsRaw::No, + } + .into(), + ); + builder.push( + Ident { + sym: Symbol::intern("Foo"), + span: Span { + range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")), + anchor, + ctx: SyntaxContextId::ROOT, + }, + is_raw: tt::IdentIsRaw::Yes, + } + .into(), + ); + builder.push(Leaf::Literal(Literal { + symbol: Symbol::intern("Foo"), + span: Span { + range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")), + anchor, + ctx: SyntaxContextId::ROOT, + }, + kind: tt::LitKind::Str, + suffix: None, + })); + builder.push(Leaf::Punct(Punct { + char: '@', + span: Span { + range: TextRange::at(TextSize::new(13), TextSize::of('@')), + anchor, + ctx: SyntaxContextId::ROOT, + }, + spacing: Spacing::Joint, + })); + builder.open( + DelimiterKind::Brace, + Span { + range: TextRange::at(TextSize::new(14), TextSize::of('{')), + anchor, + ctx: SyntaxContextId::ROOT, + }, + ); + builder.push(Leaf::Literal(Literal { + symbol: sym::INTEGER_0.clone(), + span: Span { + range: TextRange::at(TextSize::new(15), TextSize::of("0u32")), + anchor, + ctx: SyntaxContextId::ROOT, + }, + kind: tt::LitKind::Integer, + suffix: Some(sym::u32.clone()), + })); + builder.close(Span { + range: TextRange::at(TextSize::new(19), TextSize::of('}')), + anchor, + ctx: SyntaxContextId::ROOT, + }); + + builder.build() } #[test] @@ -269,7 +267,7 @@ mod tests { let mut span_data_table = Default::default(); let task = ExpandMacro { data: ExpandMacroData { - macro_body: FlatTree::new(&tt, v, &mut span_data_table), + macro_body: FlatTree::new(tt.view(), v, &mut span_data_table), macro_name: Default::default(), attributes: None, has_global_spans: ExpnGlobals { @@ -289,9 +287,8 @@ mod tests { // println!("{}", json); let back: ExpandMacro = serde_json::from_str(&json).unwrap(); - assert_eq!( - tt, - back.data.macro_body.to_subtree_resolved(v, &span_data_table), + assert!( + tt == back.data.macro_body.to_subtree_resolved(v, &span_data_table), "version: {v}" ); } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs similarity index 72% rename from src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs rename to src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs index ce4b060fca50..c194f301714f 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs @@ -1,13 +1,13 @@ -//! Serialization-friendly representation of `tt::Subtree`. +//! Serialization-friendly representation of `tt::TopSubtree`. //! -//! It is possible to serialize `Subtree` as is, as a tree, but using +//! It is possible to serialize `TopSubtree` recursively, as a tree, but using //! arbitrary-nested trees in JSON is problematic, as they can cause the JSON //! parser to overflow the stack. //! //! Additionally, such implementation would be pretty verbose, and we do care //! about performance here a bit. //! -//! So what this module does is dumping a `tt::Subtree` into a bunch of flat +//! So what this module does is dumping a `tt::TopSubtree` into a bunch of flat //! array of numbers. See the test in the parent module to get an example //! output. //! @@ -40,9 +40,11 @@ use std::collections::VecDeque; use intern::Symbol; use rustc_hash::FxHashMap; use serde_derive::{Deserialize, Serialize}; -use span::{EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange}; +use span::{ + EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TokenId, +}; -use crate::msg::{ENCODE_CLOSE_SPAN_VERSION, EXTENDED_LEAF_DATA}; +use crate::legacy_protocol::msg::{ENCODE_CLOSE_SPAN_VERSION, EXTENDED_LEAF_DATA}; pub type SpanDataIndexMap = indexmap::IndexSet>; @@ -78,15 +80,6 @@ pub fn deserialize_span_data_index_map(map: &[u32]) -> SpanDataIndexMap { .collect() } -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct TokenId(pub u32); - -impl std::fmt::Debug for TokenId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - #[derive(Serialize, Deserialize, Debug)] pub struct FlatTree { subtree: Vec, @@ -125,7 +118,7 @@ struct IdentRepr { impl FlatTree { pub fn new( - subtree: &tt::Subtree, + subtree: tt::SubtreeView<'_, Span>, version: u32, span_data_table: &mut SpanDataIndexMap, ) -> FlatTree { @@ -166,7 +159,7 @@ impl FlatTree { } } - pub fn new_raw(subtree: &tt::Subtree, version: u32) -> FlatTree { + pub fn new_raw(subtree: tt::SubtreeView<'_, TokenId>, version: u32) -> FlatTree { let mut w = Writer { string_table: FxHashMap::default(), work: VecDeque::new(), @@ -208,7 +201,7 @@ impl FlatTree { self, version: u32, span_data_table: &SpanDataIndexMap, - ) -> tt::Subtree { + ) -> tt::TopSubtree { Reader { subtree: if version >= ENCODE_CLOSE_SPAN_VERSION { read_vec(self.subtree, SubtreeRepr::read_with_close_span) @@ -234,7 +227,7 @@ impl FlatTree { .read() } - pub fn to_subtree_unresolved(self, version: u32) -> tt::Subtree { + pub fn to_subtree_unresolved(self, version: u32) -> tt::TopSubtree { Reader { subtree: if version >= ENCODE_CLOSE_SPAN_VERSION { read_vec(self.subtree, SubtreeRepr::read_with_close_span) @@ -388,7 +381,7 @@ impl InternableSpan for Span { } struct Writer<'a, 'span, S: InternableSpan> { - work: VecDeque<(usize, &'a tt::Subtree)>, + work: VecDeque<(usize, tt::iter::TtIter<'a, S>)>, string_table: FxHashMap, u32>, span_data_table: &'span mut S::Table, version: u32, @@ -402,8 +395,9 @@ struct Writer<'a, 'span, S: InternableSpan> { } impl<'a, S: InternableSpan> Writer<'a, '_, S> { - fn write(&mut self, root: &'a tt::Subtree) { - self.enqueue(root); + fn write(&mut self, root: tt::SubtreeView<'a, S>) { + let subtree = root.top_subtree(); + self.enqueue(subtree, root.iter()); while let Some((idx, subtree)) = self.work.pop_front() { self.subtree(idx, subtree); } @@ -413,20 +407,20 @@ impl<'a, S: InternableSpan> Writer<'a, '_, S> { S::token_id_of(self.span_data_table, span) } - fn subtree(&mut self, idx: usize, subtree: &'a tt::Subtree) { + fn subtree(&mut self, idx: usize, subtree: tt::iter::TtIter<'a, S>) { let mut first_tt = self.token_tree.len(); - let n_tt = subtree.token_trees.len(); + let n_tt = subtree.clone().count(); // FIXME: `count()` walks over the entire iterator. self.token_tree.resize(first_tt + n_tt, !0); self.subtree[idx].tt = [first_tt as u32, (first_tt + n_tt) as u32]; - for child in subtree.token_trees.iter() { + for child in subtree { let idx_tag = match child { - tt::TokenTree::Subtree(it) => { - let idx = self.enqueue(it); + tt::iter::TtElement::Subtree(subtree, subtree_iter) => { + let idx = self.enqueue(subtree, subtree_iter); idx << 2 } - tt::TokenTree::Leaf(leaf) => match leaf { + tt::iter::TtElement::Leaf(leaf) => match leaf { tt::Leaf::Literal(lit) => { let idx = self.literal.len() as u32; let id = self.token_id_of(lit.span); @@ -456,13 +450,13 @@ impl<'a, S: InternableSpan> Writer<'a, '_, S> { }), suffix, }); - idx << 2 | 0b01 + (idx << 2) | 0b01 } tt::Leaf::Punct(punct) => { let idx = self.punct.len() as u32; let id = self.token_id_of(punct.span); self.punct.push(PunctRepr { char: punct.char, spacing: punct.spacing, id }); - idx << 2 | 0b10 + (idx << 2) | 0b10 } tt::Leaf::Ident(ident) => { let idx = self.ident.len() as u32; @@ -475,7 +469,7 @@ impl<'a, S: InternableSpan> Writer<'a, '_, S> { self.intern(ident.sym.as_str()) }; self.ident.push(IdentRepr { id, text, is_raw: ident.is_raw.yes() }); - idx << 2 | 0b11 + (idx << 2) | 0b11 } }, }; @@ -484,13 +478,13 @@ impl<'a, S: InternableSpan> Writer<'a, '_, S> { } } - fn enqueue(&mut self, subtree: &'a tt::Subtree) -> u32 { + fn enqueue(&mut self, subtree: &'a tt::Subtree, contents: tt::iter::TtIter<'a, S>) -> u32 { let idx = self.subtree.len(); let open = self.token_id_of(subtree.delimiter.open); let close = self.token_id_of(subtree.delimiter.close); let delimiter_kind = subtree.delimiter.kind; self.subtree.push(SubtreeRepr { open, close, kind: delimiter_kind, tt: [!0, !0] }); - self.work.push_back((idx, subtree)); + self.work.push_back((idx, contents)); idx as u32 } @@ -525,103 +519,110 @@ struct Reader<'span, S: InternableSpan> { } impl Reader<'_, S> { - pub(crate) fn read(self) -> tt::Subtree { - let mut res: Vec>> = vec![None; self.subtree.len()]; + pub(crate) fn read(self) -> tt::TopSubtree { + let mut res: Vec, Vec>)>> = + vec![None; self.subtree.len()]; let read_span = |id| S::span_for_token_id(self.span_data_table, id); for i in (0..self.subtree.len()).rev() { let repr = &self.subtree[i]; let token_trees = &self.token_tree[repr.tt[0] as usize..repr.tt[1] as usize]; - let s = tt::Subtree { - delimiter: tt::Delimiter { - open: read_span(repr.open), - close: read_span(repr.close), - kind: repr.kind, - }, - token_trees: token_trees - .iter() - .copied() - .map(|idx_tag| { - let tag = idx_tag & 0b11; - let idx = (idx_tag >> 2) as usize; - match tag { - // XXX: we iterate subtrees in reverse to guarantee - // that this unwrap doesn't fire. - 0b00 => res[idx].take().unwrap().into(), - 0b01 => { - use tt::LitKind::*; - let repr = &self.literal[idx]; - let text = self.text[repr.text as usize].as_str(); - let span = read_span(repr.id); - tt::Leaf::Literal(if self.version >= EXTENDED_LEAF_DATA { - tt::Literal { - symbol: Symbol::intern(text), - span, - kind: match u16::to_le_bytes(repr.kind) { - [0, _] => Err(()), - [1, _] => Byte, - [2, _] => Char, - [3, _] => Integer, - [4, _] => Float, - [5, _] => Str, - [6, r] => StrRaw(r), - [7, _] => ByteStr, - [8, r] => ByteStrRaw(r), - [9, _] => CStr, - [10, r] => CStrRaw(r), - _ => unreachable!(), - }, - suffix: if repr.suffix != !0 { - Some(Symbol::intern( - self.text[repr.suffix as usize].as_str(), - )) - } else { - None - }, - } - } else { - tt::token_to_literal(text, span) - }) - .into() - } - 0b10 => { - let repr = &self.punct[idx]; - tt::Leaf::Punct(tt::Punct { - char: repr.char, - spacing: repr.spacing, - span: read_span(repr.id), - }) - .into() - } - 0b11 => { - let repr = &self.ident[idx]; - let text = self.text[repr.text as usize].as_str(); - let (is_raw, text) = if self.version >= EXTENDED_LEAF_DATA { - ( - if repr.is_raw { - tt::IdentIsRaw::Yes - } else { - tt::IdentIsRaw::No - }, - text, - ) - } else { - tt::IdentIsRaw::split_from_symbol(text) - }; - tt::Leaf::Ident(tt::Ident { - sym: Symbol::intern(text), - span: read_span(repr.id), - is_raw, - }) - .into() - } - other => panic!("bad tag: {other}"), - } - }) - .collect(), + let delimiter = tt::Delimiter { + open: read_span(repr.open), + close: read_span(repr.close), + kind: repr.kind, }; - res[i] = Some(s); + let mut s = Vec::new(); + for &idx_tag in token_trees { + let tag = idx_tag & 0b11; + let idx = (idx_tag >> 2) as usize; + match tag { + // XXX: we iterate subtrees in reverse to guarantee + // that this unwrap doesn't fire. + 0b00 => { + let (delimiter, subtree) = res[idx].take().unwrap(); + s.push(tt::TokenTree::Subtree(tt::Subtree { + delimiter, + len: subtree.len() as u32, + })); + s.extend(subtree) + } + 0b01 => { + use tt::LitKind::*; + let repr = &self.literal[idx]; + let text = self.text[repr.text as usize].as_str(); + let span = read_span(repr.id); + s.push( + tt::Leaf::Literal(if self.version >= EXTENDED_LEAF_DATA { + tt::Literal { + symbol: Symbol::intern(text), + span, + kind: match u16::to_le_bytes(repr.kind) { + [0, _] => Err(()), + [1, _] => Byte, + [2, _] => Char, + [3, _] => Integer, + [4, _] => Float, + [5, _] => Str, + [6, r] => StrRaw(r), + [7, _] => ByteStr, + [8, r] => ByteStrRaw(r), + [9, _] => CStr, + [10, r] => CStrRaw(r), + _ => unreachable!(), + }, + suffix: if repr.suffix != !0 { + Some(Symbol::intern( + self.text[repr.suffix as usize].as_str(), + )) + } else { + None + }, + } + } else { + tt::token_to_literal(text, span) + }) + .into(), + ) + } + 0b10 => { + let repr = &self.punct[idx]; + s.push( + tt::Leaf::Punct(tt::Punct { + char: repr.char, + spacing: repr.spacing, + span: read_span(repr.id), + }) + .into(), + ) + } + 0b11 => { + let repr = &self.ident[idx]; + let text = self.text[repr.text as usize].as_str(); + let (is_raw, text) = if self.version >= EXTENDED_LEAF_DATA { + ( + if repr.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No }, + text, + ) + } else { + tt::IdentIsRaw::split_from_symbol(text) + }; + s.push( + tt::Leaf::Ident(tt::Ident { + sym: Symbol::intern(text), + span: read_span(repr.id), + is_raw, + }) + .into(), + ) + } + other => panic!("bad tag: {other}"), + } + } + res[i] = Some((delimiter, s)); } - res[0].take().unwrap() + let (delimiter, mut res) = res[0].take().unwrap(); + res.insert(0, tt::TokenTree::Subtree(tt::Subtree { delimiter, len: res.len() as u32 })); + tt::TopSubtree(res.into_boxed_slice()) } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index e54d501b94cc..dc3328ebcda4 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -5,26 +5,26 @@ //! is used to provide basic infrastructure for communication between two //! processes: Client (RA itself), Server (the external program) -pub mod json; -pub mod msg; +pub mod legacy_protocol { + pub mod json; + pub mod msg; +} mod process; use paths::{AbsPath, AbsPathBuf}; use span::Span; use std::{fmt, io, sync::Arc}; -use serde::{Deserialize, Serialize}; - use crate::{ - msg::{ + legacy_protocol::msg::{ deserialize_span_data_index_map, flat::serialize_span_data_index_map, ExpandMacro, - ExpnGlobals, FlatTree, PanicMessage, SpanDataIndexMap, HAS_GLOBAL_SPANS, - RUST_ANALYZER_SPAN_SUPPORT, + ExpandMacroData, ExpnGlobals, FlatTree, PanicMessage, Request, Response, SpanDataIndexMap, + HAS_GLOBAL_SPANS, RUST_ANALYZER_SPAN_SUPPORT, }, - process::ProcMacroProcessSrv, + process::ProcMacroServerProcess, }; -#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, serde_derive::Serialize, serde_derive::Deserialize)] pub enum ProcMacroKind { CustomDerive, Attr, @@ -37,12 +37,12 @@ pub enum ProcMacroKind { /// A handle to an external process which load dylibs with macros (.so or .dll) /// and runs actual macro expansion functions. #[derive(Debug)] -pub struct ProcMacroServer { +pub struct ProcMacroClient { /// Currently, the proc macro process expands all procedural macros sequentially. /// /// That means that concurrent salsa requests may block each other when expanding proc macros, /// which is unfortunate, but simple and good enough for the time being. - process: Arc, + process: Arc, path: AbsPathBuf, } @@ -56,13 +56,13 @@ impl MacroDylib { } } -/// A handle to a specific macro (a `#[proc_macro]` annotated function). +/// A handle to a specific proc-macro (a `#[proc_macro]` annotated function). /// -/// It exists within a context of a specific [`ProcMacroProcess`] -- currently -/// we share a single expander process for all macros. +/// It exists within the context of a specific proc-macro server -- currently +/// we share a single expander process for all macros within a workspace. #[derive(Debug, Clone)] pub struct ProcMacro { - process: Arc, + process: Arc, dylib_path: Arc, name: Box, kind: ProcMacroKind, @@ -95,21 +95,22 @@ impl fmt::Display for ServerError { } } -impl ProcMacroServer { +impl ProcMacroClient { /// Spawns an external process as the proc macro server and returns a client connected to it. pub fn spawn( process_path: &AbsPath, env: impl IntoIterator, impl AsRef)> + Clone, - ) -> io::Result { - let process = ProcMacroProcessSrv::run(process_path, env)?; - Ok(ProcMacroServer { process: Arc::new(process), path: process_path.to_owned() }) + ) -> io::Result { + let process = ProcMacroServerProcess::run(process_path, env)?; + Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() }) } - pub fn path(&self) -> &AbsPath { + pub fn server_path(&self) -> &AbsPath { &self.path } + /// Loads a proc-macro dylib into the server process returning a list of `ProcMacro`s loaded. pub fn load_dylib(&self, dylib: MacroDylib) -> Result, ServerError> { let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered(); let macros = self.process.find_proc_macros(&dylib.path)?; @@ -145,14 +146,14 @@ impl ProcMacro { pub fn expand( &self, - subtree: &tt::Subtree, - attr: Option<&tt::Subtree>, + subtree: tt::SubtreeView<'_, Span>, + attr: Option>, env: Vec<(String, String)>, def_site: Span, call_site: Span, mixed_site: Span, current_dir: Option, - ) -> Result, PanicMessage>, ServerError> { + ) -> Result, PanicMessage>, ServerError> { let version = self.process.version(); let mut span_data_table = SpanDataIndexMap::default(); @@ -160,7 +161,7 @@ impl ProcMacro { let call_site = span_data_table.insert_full(call_site).0; let mixed_site = span_data_table.insert_full(mixed_site).0; let task = ExpandMacro { - data: msg::ExpandMacroData { + data: ExpandMacroData { macro_body: FlatTree::new(subtree, version, &mut span_data_table), macro_name: self.name.to_string(), attributes: attr @@ -182,13 +183,13 @@ impl ProcMacro { current_dir, }; - let response = self.process.send_task(msg::Request::ExpandMacro(Box::new(task)))?; + let response = self.process.send_task(Request::ExpandMacro(Box::new(task)))?; match response { - msg::Response::ExpandMacro(it) => { + Response::ExpandMacro(it) => { Ok(it.map(|tree| FlatTree::to_subtree_resolved(tree, version, &span_data_table))) } - msg::Response::ExpandMacroExtended(it) => Ok(it.map(|resp| { + Response::ExpandMacroExtended(it) => Ok(it.map(|resp| { FlatTree::to_subtree_resolved( resp.tree, version, diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index 4045e25fdf11..d998b23d3bbe 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -11,13 +11,18 @@ use paths::AbsPath; use stdx::JodChild; use crate::{ - json::{read_json, write_json}, - msg::{Message, Request, Response, SpanMode, CURRENT_API_VERSION, RUST_ANALYZER_SPAN_SUPPORT}, + legacy_protocol::{ + json::{read_json, write_json}, + msg::{ + Message, Request, Response, ServerConfig, SpanMode, CURRENT_API_VERSION, + RUST_ANALYZER_SPAN_SUPPORT, + }, + }, ProcMacroKind, ServerError, }; #[derive(Debug)] -pub(crate) struct ProcMacroProcessSrv { +pub(crate) struct ProcMacroServerProcess { /// The state of the proc-macro server process, the protocol is currently strictly sequential /// hence the lock on the state. state: Mutex, @@ -34,24 +39,24 @@ struct ProcessSrvState { stdout: BufReader, } -impl ProcMacroProcessSrv { +impl ProcMacroServerProcess { pub(crate) fn run( process_path: &AbsPath, env: impl IntoIterator, impl AsRef)> + Clone, - ) -> io::Result { - let create_srv = |null_stderr| { - let mut process = Process::run(process_path, env.clone(), null_stderr)?; + ) -> io::Result { + let create_srv = || { + let mut process = Process::run(process_path, env.clone())?; let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); - io::Result::Ok(ProcMacroProcessSrv { + io::Result::Ok(ProcMacroServerProcess { state: Mutex::new(ProcessSrvState { process, stdin, stdout }), version: 0, mode: SpanMode::Id, exited: OnceLock::new(), }) }; - let mut srv = create_srv(true)?; + let mut srv = create_srv()?; tracing::info!("sending proc-macro server version check"); match srv.version_check() { Ok(v) if v > CURRENT_API_VERSION => Err(io::Error::new( @@ -62,7 +67,6 @@ impl ProcMacroProcessSrv { )), Ok(v) => { tracing::info!("Proc-macro server version: {v}"); - srv = create_srv(false)?; srv.version = v; if srv.version >= RUST_ANALYZER_SPAN_SUPPORT { if let Ok(mode) = srv.enable_rust_analyzer_spans() { @@ -73,8 +77,10 @@ impl ProcMacroProcessSrv { Ok(srv) } Err(e) => { - tracing::info!(%e, "proc-macro version check failed, restarting and assuming version 0"); - create_srv(false) + tracing::info!(%e, "proc-macro version check failed"); + Err( + io::Error::new(io::ErrorKind::Other, format!("proc-macro server version check failed: {e}")), + ) } } } @@ -98,13 +104,11 @@ impl ProcMacroProcessSrv { } fn enable_rust_analyzer_spans(&self) -> Result { - let request = Request::SetConfig(crate::msg::ServerConfig { - span_mode: crate::msg::SpanMode::RustAnalyzer, - }); + let request = Request::SetConfig(ServerConfig { span_mode: SpanMode::RustAnalyzer }); let response = self.send_task(request)?; match response { - Response::SetConfig(crate::msg::ServerConfig { span_mode }) => Ok(span_mode), + Response::SetConfig(ServerConfig { span_mode }) => Ok(span_mode), _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), } } @@ -182,9 +186,8 @@ impl Process { fn run( path: &AbsPath, env: impl IntoIterator, impl AsRef)>, - null_stderr: bool, ) -> io::Result { - let child = JodChild(mk_child(path, env, null_stderr)?); + let child = JodChild(mk_child(path, env)?); Ok(Process { child }) } @@ -200,14 +203,14 @@ impl Process { fn mk_child( path: &AbsPath, env: impl IntoIterator, impl AsRef)>, - null_stderr: bool, ) -> io::Result { + #[allow(clippy::disallowed_methods)] let mut cmd = Command::new(path); cmd.envs(env) .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable") .stdin(Stdio::piped()) .stdout(Stdio::piped()) - .stderr(if null_stderr { Stdio::null() } else { Stdio::inherit() }); + .stderr(Stdio::inherit()); if cfg!(windows) { let mut path_var = std::ffi::OsString::new(); path_var.push(path.parent().unwrap().parent().unwrap()); 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 1c394513c459..57a28b00365f 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 @@ -12,6 +12,7 @@ rust-version.workspace = true [dependencies] proc-macro-srv.workspace = true proc-macro-api.workspace = true +tt.workspace = true [features] sysroot-abi = ["proc-macro-srv/sysroot-abi"] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs index 137efd5e7a05..de59e88aac40 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs @@ -6,7 +6,10 @@ #[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; -use std::io; +#[cfg(any(feature = "sysroot-abi", rust_analyzer))] +mod main_loop; +#[cfg(any(feature = "sysroot-abi", rust_analyzer))] +use main_loop::run; fn main() -> std::io::Result<()> { let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE"); @@ -22,57 +25,10 @@ fn main() -> std::io::Result<()> { } #[cfg(not(any(feature = "sysroot-abi", rust_analyzer)))] -fn run() -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Unsupported, +fn run() -> std::io::Result<()> { + Err(std::io::Error::new( + std::io::ErrorKind::Unsupported, "proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function" .to_owned(), )) } - -#[cfg(any(feature = "sysroot-abi", rust_analyzer))] -fn run() -> io::Result<()> { - use proc_macro_api::{ - json::{read_json, write_json}, - msg::{self, Message}, - }; - use proc_macro_srv::EnvSnapshot; - - let read_request = - |buf: &mut String| msg::Request::read(read_json, &mut io::stdin().lock(), buf); - - let write_response = |msg: msg::Response| msg.write(write_json, &mut io::stdout().lock()); - - let env = EnvSnapshot::default(); - let mut srv = proc_macro_srv::ProcMacroSrv::new(&env); - let mut buf = String::new(); - - while let Some(req) = read_request(&mut buf)? { - let res = match req { - msg::Request::ListMacros { dylib_path } => { - msg::Response::ListMacros(srv.list_macros(&dylib_path)) - } - msg::Request::ExpandMacro(task) => match srv.span_mode() { - msg::SpanMode::Id => { - msg::Response::ExpandMacro(srv.expand(*task).map(|(it, _)| it)) - } - msg::SpanMode::RustAnalyzer => msg::Response::ExpandMacroExtended( - srv.expand(*task).map(|(tree, span_data_table)| msg::ExpandMacroExtended { - tree, - span_data_table, - }), - ), - }, - msg::Request::ApiVersionCheck {} => { - msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION) - } - msg::Request::SetConfig(config) => { - srv.set_span_mode(config.span_mode); - msg::Response::SetConfig(config) - } - }; - write_response(res)? - } - - Ok(()) -} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs new file mode 100644 index 000000000000..ba1fcd8e336a --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -0,0 +1,134 @@ +//! The main loop of the proc-macro server. +use std::io; + +use proc_macro_api::legacy_protocol::{ + json::{read_json, write_json}, + msg::{ + self, deserialize_span_data_index_map, serialize_span_data_index_map, ExpandMacroData, + ExpnGlobals, Message, SpanMode, TokenId, CURRENT_API_VERSION, + }, +}; +use proc_macro_srv::EnvSnapshot; + +pub(crate) fn run() -> io::Result<()> { + fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind { + match kind { + proc_macro_srv::ProcMacroKind::CustomDerive => { + proc_macro_api::ProcMacroKind::CustomDerive + } + proc_macro_srv::ProcMacroKind::Bang => proc_macro_api::ProcMacroKind::Bang, + proc_macro_srv::ProcMacroKind::Attr => proc_macro_api::ProcMacroKind::Attr, + } + } + + let read_request = + |buf: &mut String| msg::Request::read(read_json, &mut io::stdin().lock(), buf); + + let write_response = |msg: msg::Response| msg.write(write_json, &mut io::stdout().lock()); + + let env = EnvSnapshot::default(); + let mut srv = proc_macro_srv::ProcMacroSrv::new(&env); + let mut buf = String::new(); + + let mut span_mode = SpanMode::Id; + + while let Some(req) = read_request(&mut buf)? { + let res = match req { + msg::Request::ListMacros { dylib_path } => { + msg::Response::ListMacros(srv.list_macros(&dylib_path).map(|macros| { + macros.into_iter().map(|(name, kind)| (name, macro_kind_to_api(kind))).collect() + })) + } + msg::Request::ExpandMacro(task) => { + let msg::ExpandMacro { + lib, + env, + current_dir, + data: + ExpandMacroData { + macro_body, + macro_name, + attributes, + has_global_spans: + ExpnGlobals { serialize: _, def_site, call_site, mixed_site }, + span_data_table, + }, + } = *task; + match span_mode { + SpanMode::Id => msg::Response::ExpandMacro({ + let def_site = TokenId(def_site as u32); + let call_site = TokenId(call_site as u32); + let mixed_site = TokenId(mixed_site as u32); + + let macro_body = macro_body.to_subtree_unresolved(CURRENT_API_VERSION); + let attributes = + attributes.map(|it| it.to_subtree_unresolved(CURRENT_API_VERSION)); + + srv.expand( + lib, + env, + current_dir, + macro_name, + macro_body, + attributes, + def_site, + call_site, + mixed_site, + ) + .map(|it| { + msg::FlatTree::new_raw(tt::SubtreeView::new(&it), CURRENT_API_VERSION) + }) + .map_err(msg::PanicMessage) + }), + SpanMode::RustAnalyzer => msg::Response::ExpandMacroExtended({ + let mut span_data_table = deserialize_span_data_index_map(&span_data_table); + + let def_site = span_data_table[def_site]; + let call_site = span_data_table[call_site]; + let mixed_site = span_data_table[mixed_site]; + + let macro_body = + macro_body.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table); + let attributes = attributes.map(|it| { + it.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table) + }); + srv.expand( + lib, + env, + current_dir, + macro_name, + macro_body, + attributes, + def_site, + call_site, + mixed_site, + ) + .map(|it| { + ( + msg::FlatTree::new( + tt::SubtreeView::new(&it), + CURRENT_API_VERSION, + &mut span_data_table, + ), + serialize_span_data_index_map(&span_data_table), + ) + }) + .map(|(tree, span_data_table)| msg::ExpandMacroExtended { + tree, + span_data_table, + }) + .map_err(msg::PanicMessage) + }), + } + } + msg::Request::ApiVersionCheck {} => msg::Response::ApiVersionCheck(CURRENT_API_VERSION), + msg::Request::SetConfig(config) => { + span_mode = config.span_mode; + msg::Response::SetConfig(config) + } + }; + write_response(res)? + } + + Ok(()) +} 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 983859694599..00695c547372 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -23,7 +23,6 @@ syntax-bridge.workspace = true paths.workspace = true # span = {workspace = true, default-features = false} does not work span = { path = "../span", version = "0.0.0", default-features = false} -proc-macro-api.workspace = true intern.workspace = true ra-ap-rustc_lexer.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs index 9a17cfc9f360..07a10aaae578 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs @@ -7,6 +7,7 @@ fn main() { println!("cargo::rustc-check-cfg=cfg(rust_analyzer)"); let rustc = env::var("RUSTC").expect("proc-macro-srv's build script expects RUSTC to be set"); + #[allow(clippy::disallowed_methods)] let output = Command::new(rustc).arg("--version").output().expect("rustc --version must run"); let version_string = std::str::from_utf8(&output.stdout[..]) .expect("rustc --version output must be UTF-8") diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs index ff2f5d186391..d3d58a6df011 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs @@ -7,6 +7,8 @@ //! a specific rustup toolchain: this allows testing against older ABIs (e.g. //! 1.58) and future ABIs (stage1, nightly) +#![allow(clippy::disallowed_methods)] + use std::{ env, path::{Path, PathBuf}, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs index 26f6af84aaeb..fe15d42b4e48 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs @@ -8,39 +8,8 @@ use std::{fmt, fs, io, time::SystemTime}; use libloading::Library; use object::Object; use paths::{Utf8Path, Utf8PathBuf}; -use proc_macro_api::ProcMacroKind; -use crate::ProcMacroSrvSpan; - -const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; - -fn invalid_data_err(e: impl Into>) -> io::Error { - io::Error::new(io::ErrorKind::InvalidData, e) -} - -fn is_derive_registrar_symbol(symbol: &str) -> bool { - symbol.contains(NEW_REGISTRAR_SYMBOL) -} - -fn find_registrar_symbol(obj: &object::File<'_>) -> object::Result> { - Ok(obj - .exports()? - .into_iter() - .map(|export| export.name()) - .filter_map(|sym| String::from_utf8(sym.into()).ok()) - .find(|sym| is_derive_registrar_symbol(sym)) - .map(|sym| { - // From MacOS docs: - // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html - // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be - // prepended with an underscore. - if cfg!(target_os = "macos") && sym.starts_with('_') { - sym[1..].to_owned() - } else { - sym - } - })) -} +use crate::{proc_macros::ProcMacros, server_impl::TopSubtree, ProcMacroKind, ProcMacroSrvSpan}; /// Loads dynamic library in platform dependent manner. /// @@ -100,13 +69,14 @@ impl From for LoadProcMacroDylibError { } } -struct ProcMacroLibraryLibloading { +struct ProcMacroLibrary { + // 'static is actually the lifetime of library, so make sure this drops before _lib + proc_macros: &'static ProcMacros, // Hold on to the library so it doesn't unload _lib: Library, - proc_macros: crate::proc_macros::ProcMacros, } -impl ProcMacroLibraryLibloading { +impl ProcMacroLibrary { fn open(path: &Utf8Path) -> Result { let file = fs::File::open(path)?; let file = unsafe { memmap2::Mmap::map(&file) }?; @@ -119,27 +89,22 @@ impl ProcMacroLibraryLibloading { })?; let lib = load_library(path).map_err(invalid_data_err)?; - let proc_macros = crate::proc_macros::ProcMacros::from_lib( - &lib, - symbol_name, - &version_info.version_string, - )?; - Ok(ProcMacroLibraryLibloading { _lib: lib, proc_macros }) - } -} - -struct RemoveFileOnDrop(Utf8PathBuf); -impl Drop for RemoveFileOnDrop { - fn drop(&mut self) { - #[cfg(windows)] - std::fs::remove_file(&self.0).unwrap(); - _ = self.0; + let proc_macros = unsafe { + // SAFETY: We extend the lifetime here to avoid referential borrow problems + // We never reveal proc_macros to the outside and drop it before _lib + std::mem::transmute::<&ProcMacros, &'static ProcMacros>(ProcMacros::from_lib( + &lib, + symbol_name, + &version_info.version_string, + )?) + }; + Ok(ProcMacroLibrary { _lib: lib, proc_macros }) } } // Drop order matters as we can't remove the dylib before the library is unloaded pub(crate) struct Expander { - inner: ProcMacroLibraryLibloading, + inner: ProcMacroLibrary, _remove_on_drop: RemoveFileOnDrop, modified_time: SystemTime, } @@ -152,7 +117,7 @@ impl Expander { let modified_time = fs::metadata(&lib).and_then(|it| it.modified())?; let path = ensure_file_with_lock_free_access(&lib)?; - let library = ProcMacroLibraryLibloading::open(path.as_ref())?; + let library = ProcMacroLibrary::open(path.as_ref())?; Ok(Expander { inner: library, _remove_on_drop: RemoveFileOnDrop(path), modified_time }) } @@ -160,12 +125,12 @@ impl Expander { pub(crate) fn expand( &self, macro_name: &str, - macro_body: tt::Subtree, - attributes: Option>, + macro_body: TopSubtree, + attributes: Option>, def_site: S, call_site: S, mixed_site: S, - ) -> Result, String> + ) -> Result, String> where ::TokenStream: Default, { @@ -185,6 +150,44 @@ impl Expander { } } +fn invalid_data_err(e: impl Into>) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, e) +} + +fn is_derive_registrar_symbol(symbol: &str) -> bool { + const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; + symbol.contains(NEW_REGISTRAR_SYMBOL) +} + +fn find_registrar_symbol(obj: &object::File<'_>) -> object::Result> { + Ok(obj + .exports()? + .into_iter() + .map(|export| export.name()) + .filter_map(|sym| String::from_utf8(sym.into()).ok()) + .find(|sym| is_derive_registrar_symbol(sym)) + .map(|sym| { + // From MacOS docs: + // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html + // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be + // prepended with an underscore. + if cfg!(target_os = "macos") && sym.starts_with('_') { + sym[1..].to_owned() + } else { + sym + } + })) +} + +struct RemoveFileOnDrop(Utf8PathBuf); +impl Drop for RemoveFileOnDrop { + fn drop(&mut self) { + #[cfg(windows)] + std::fs::remove_file(&self.0).unwrap(); + _ = self.0; + } +} + /// Copy the dylib to temp directory to prevent locking in Windows #[cfg(windows)] fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 85833dab1b09..7ae75713ebfe 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -14,6 +14,7 @@ #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)] #![allow(unreachable_pub, internal_features, clippy::disallowed_types, clippy::print_stderr)] +#![deny(deprecated_safe)] extern crate proc_macro; #[cfg(feature = "in-rust-tree")] @@ -38,62 +39,82 @@ use std::{ }; use paths::{Utf8Path, Utf8PathBuf}; -use proc_macro_api::{ - msg::{ - self, deserialize_span_data_index_map, serialize_span_data_index_map, ExpnGlobals, - SpanMode, TokenId, CURRENT_API_VERSION, - }, - ProcMacroKind, -}; -use span::Span; +use span::{Span, TokenId}; use crate::server_impl::TokenStream; +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum ProcMacroKind { + CustomDerive, + Attr, + Bang, +} + pub const RUSTC_VERSION_STRING: &str = env!("RUSTC_VERSION"); pub struct ProcMacroSrv<'env> { expanders: HashMap, - span_mode: SpanMode, env: &'env EnvSnapshot, } impl<'env> ProcMacroSrv<'env> { pub fn new(env: &'env EnvSnapshot) -> Self { - Self { expanders: Default::default(), span_mode: Default::default(), env } + Self { expanders: Default::default(), env } } } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; impl ProcMacroSrv<'_> { - pub fn set_span_mode(&mut self, span_mode: SpanMode) { - self.span_mode = span_mode; - } - - pub fn span_mode(&self) -> SpanMode { - self.span_mode - } - - pub fn expand( + pub fn expand( &mut self, - msg::ExpandMacro { lib, env, current_dir, data }: msg::ExpandMacro, - ) -> Result<(msg::FlatTree, Vec), msg::PanicMessage> { - let span_mode = self.span_mode; + lib: impl AsRef, + env: Vec<(String, String)>, + current_dir: Option>, + macro_name: String, + macro_body: tt::TopSubtree, + attribute: Option>, + def_site: S, + call_site: S, + mixed_site: S, + ) -> Result>, String> { let snapped_env = self.env; - let expander = self - .expander(lib.as_ref()) - .map_err(|err| msg::PanicMessage(format!("failed to load macro: {err}")))?; + let expander = + self.expander(lib.as_ref()).map_err(|err| format!("failed to load macro: {err}"))?; let prev_env = EnvChange::apply(snapped_env, env, current_dir.as_ref().map(<_>::as_ref)); - let result = match span_mode { - SpanMode::Id => expand_id(data, expander).map(|it| (it, vec![])), - SpanMode::RustAnalyzer => expand_ra_span(data, expander), - }; + // Note, we spawn a new thread here so that thread locals allocation don't accumulate (this + // includes the proc-macro symbol interner) + let result = thread::scope(|s| { + let thread = thread::Builder::new() + .stack_size(EXPANDER_STACK_SIZE) + .name(macro_name.clone()) + .spawn_scoped(s, move || { + expander + .expand( + ¯o_name, + server_impl::TopSubtree(macro_body.0.into_vec()), + attribute.map(|it| server_impl::TopSubtree(it.0.into_vec())), + def_site, + call_site, + mixed_site, + ) + .map(|tt| tt.0) + }); + let res = match thread { + Ok(handle) => handle.join(), + Err(e) => return Err(e.to_string()), + }; + match res { + Ok(res) => res, + Err(e) => std::panic::resume_unwind(e), + } + }); prev_env.rollback(); - result.map_err(msg::PanicMessage) + result } pub fn list_macros( @@ -123,7 +144,7 @@ impl ProcMacroSrv<'_> { } } -trait ProcMacroSrvSpan: Copy { +pub trait ProcMacroSrvSpan: Copy + Send { type Server: proc_macro::bridge::server::Server>; fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server; } @@ -147,93 +168,6 @@ impl ProcMacroSrvSpan for Span { } } } - -fn expand_id( - msg::ExpandMacroData { - macro_body, - macro_name, - attributes, - has_global_spans: ExpnGlobals { serialize: _, def_site, call_site, mixed_site }, - span_data_table: _, - }: msg::ExpandMacroData, - expander: &dylib::Expander, -) -> Result { - let def_site = TokenId(def_site as u32); - let call_site = TokenId(call_site as u32); - let mixed_site = TokenId(mixed_site as u32); - - let macro_body = macro_body.to_subtree_unresolved(CURRENT_API_VERSION); - let attributes = attributes.map(|it| it.to_subtree_unresolved(CURRENT_API_VERSION)); - let result = thread::scope(|s| { - let thread = thread::Builder::new() - .stack_size(EXPANDER_STACK_SIZE) - .name(macro_name.clone()) - .spawn_scoped(s, || { - expander - .expand(¯o_name, macro_body, attributes, def_site, call_site, mixed_site) - .map(|it| msg::FlatTree::new_raw(&it, CURRENT_API_VERSION)) - }); - let res = match thread { - Ok(handle) => handle.join(), - Err(e) => std::panic::resume_unwind(Box::new(e)), - }; - - match res { - Ok(res) => res, - Err(e) => std::panic::resume_unwind(e), - } - }); - result -} - -fn expand_ra_span( - msg::ExpandMacroData { - macro_body, - macro_name, - attributes, - has_global_spans: ExpnGlobals { serialize: _, def_site, call_site, mixed_site }, - span_data_table, - }: msg::ExpandMacroData, - expander: &dylib::Expander, -) -> Result<(msg::FlatTree, Vec), String> { - let mut span_data_table = deserialize_span_data_index_map(&span_data_table); - - let def_site = span_data_table[def_site]; - let call_site = span_data_table[call_site]; - let mixed_site = span_data_table[mixed_site]; - - let macro_body = macro_body.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table); - let attributes = - attributes.map(|it| it.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table)); - // Note, we spawn a new thread here so that thread locals allocation don't accumulate (this - // includes the proc-macro symbol interner) - let result = thread::scope(|s| { - let thread = thread::Builder::new() - .stack_size(EXPANDER_STACK_SIZE) - .name(macro_name.clone()) - .spawn_scoped(s, || { - expander - .expand(¯o_name, macro_body, attributes, def_site, call_site, mixed_site) - .map(|it| { - ( - msg::FlatTree::new(&it, CURRENT_API_VERSION, &mut span_data_table), - serialize_span_data_index_map(&span_data_table), - ) - }) - }); - let res = match thread { - Ok(handle) => handle.join(), - Err(e) => std::panic::resume_unwind(Box::new(e)), - }; - - match res { - Ok(res) => res, - Err(e) => std::panic::resume_unwind(e), - } - }); - result -} - pub struct PanicMessage { message: Option, } @@ -254,10 +188,13 @@ impl Default for EnvSnapshot { } } +static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(()); + struct EnvChange<'snap> { changed_vars: Vec, prev_working_dir: Option, snap: &'snap EnvSnapshot, + _guard: std::sync::MutexGuard<'snap, ()>, } impl<'snap> EnvChange<'snap> { @@ -266,6 +203,7 @@ impl<'snap> EnvChange<'snap> { new_vars: Vec<(String, String)>, current_dir: Option<&Path>, ) -> EnvChange<'snap> { + let guard = ENV_LOCK.lock().unwrap_or_else(std::sync::PoisonError::into_inner); let prev_working_dir = match current_dir { Some(dir) => { let prev_working_dir = std::env::current_dir().ok(); @@ -284,11 +222,13 @@ impl<'snap> EnvChange<'snap> { changed_vars: new_vars .into_iter() .map(|(k, v)| { - env::set_var(&k, v); + // SAFETY: We have acquired the environment lock + unsafe { env::set_var(&k, v) }; k }) .collect(), prev_working_dir, + _guard: guard, } } @@ -298,9 +238,12 @@ impl<'snap> EnvChange<'snap> { impl Drop for EnvChange<'_> { fn drop(&mut self) { for name in self.changed_vars.drain(..) { - match self.snap.vars.get::(name.as_ref()) { - Some(prev_val) => env::set_var(name, prev_val), - None => env::remove_var(name), + // SAFETY: We have acquired the environment lock + unsafe { + match self.snap.vars.get::(name.as_ref()) { + Some(prev_val) => env::set_var(name, prev_val), + None => env::remove_var(name), + } } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs index 097b39a3f912..58f5e80dc4ea 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs @@ -1,15 +1,15 @@ //! Proc macro ABI use proc_macro::bridge; -use proc_macro_api::ProcMacroKind; use libloading::Library; -use crate::{dylib::LoadProcMacroDylibError, ProcMacroSrvSpan}; +use crate::{ + dylib::LoadProcMacroDylibError, server_impl::TopSubtree, ProcMacroKind, ProcMacroSrvSpan, +}; -pub(crate) struct ProcMacros { - exported_macros: Vec, -} +#[repr(transparent)] +pub(crate) struct ProcMacros([bridge::client::ProcMacro]); impl From for crate::PanicMessage { fn from(p: bridge::PanicMessage) -> Self { @@ -27,29 +27,28 @@ impl ProcMacros { /// *`info` - RustCInfo about the compiler that was used to compile the /// macro crate. This is the information we use to figure out /// which ABI to return - pub(crate) fn from_lib( - lib: &Library, + pub(crate) fn from_lib<'l>( + lib: &'l Library, symbol_name: String, version_string: &str, - ) -> Result { - if version_string == crate::RUSTC_VERSION_STRING { - let macros = - unsafe { lib.get::<&&[bridge::client::ProcMacro]>(symbol_name.as_bytes()) }?; - - return Ok(Self { exported_macros: macros.to_vec() }); + ) -> Result<&'l ProcMacros, LoadProcMacroDylibError> { + if version_string != crate::RUSTC_VERSION_STRING { + return Err(LoadProcMacroDylibError::AbiMismatch(version_string.to_owned())); } - Err(LoadProcMacroDylibError::AbiMismatch(version_string.to_owned())) + unsafe { lib.get::<&'l &'l ProcMacros>(symbol_name.as_bytes()) } + .map(|it| **it) + .map_err(Into::into) } pub(crate) fn expand( &self, macro_name: &str, - macro_body: tt::Subtree, - attributes: Option>, + macro_body: TopSubtree, + attributes: Option>, def_site: S, call_site: S, mixed_site: S, - ) -> Result, crate::PanicMessage> { + ) -> Result, crate::PanicMessage> { let parsed_body = crate::server_impl::TokenStream::with_subtree(macro_body); let parsed_attributes = attributes @@ -57,7 +56,7 @@ impl ProcMacros { crate::server_impl::TokenStream::with_subtree(attr) }); - for proc_macro in &self.exported_macros { + for proc_macro in &self.0 { match proc_macro { bridge::client::ProcMacro::CustomDerive { trait_name, client, .. } if *trait_name == macro_name => @@ -103,7 +102,7 @@ impl ProcMacros { } pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { - self.exported_macros + self.0 .iter() .map(|proc_macro| match proc_macro { bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs index c9a862169055..3d999421794b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs @@ -8,6 +8,8 @@ //! //! FIXME: No span and source file information is implemented yet +use std::fmt; + use proc_macro::bridge; mod token_stream; @@ -19,6 +21,32 @@ pub mod token_id; // pub use symbol::*; use tt::Spacing; +#[derive(Clone)] +pub(crate) struct TopSubtree(pub(crate) Vec>); + +impl fmt::Debug for TopSubtree { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&tt::TokenTreesView::new(&self.0), f) + } +} + +impl TopSubtree { + pub(crate) fn top_subtree(&self) -> &tt::Subtree { + let tt::TokenTree::Subtree(subtree) = &self.0[0] else { + unreachable!("the first token tree is always the top subtree"); + }; + subtree + } + + pub(crate) fn from_bridge(group: bridge::Group, S>) -> Self { + let delimiter = delim_to_internal(group.delimiter, group.span); + let mut tts = + group.stream.map(|it| it.token_trees).unwrap_or_else(|| Vec::with_capacity(1)); + tts.insert(0, tt::TokenTree::Subtree(tt::Subtree { delimiter, len: tts.len() as u32 })); + TopSubtree(tts) + } +} + fn delim_to_internal(d: proc_macro::Delimiter, span: bridge::DelimSpan) -> tt::Delimiter { let kind = match d { proc_macro::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 1b535d2a1ccc..beaebf33300d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -6,23 +6,18 @@ //! change their representation to be compatible with rust-analyzer's. use std::{ collections::{HashMap, HashSet}, - iter, ops::{Bound, Range}, }; use intern::Symbol; use proc_macro::bridge::{self, server}; -use span::{Span, FIXUP_ERASED_FILE_AST_ID_MARKER}; +use span::{FileId, Span, FIXUP_ERASED_FILE_AST_ID_MARKER}; use tt::{TextRange, TextSize}; -use crate::server_impl::{ - delim_to_external, delim_to_internal, literal_kind_to_external, literal_kind_to_internal, - token_stream::TokenStreamBuilder, -}; +use crate::server_impl::{literal_kind_to_internal, token_stream::TokenStreamBuilder, TopSubtree}; mod tt { pub use tt::*; - pub type Subtree = ::tt::Subtree; pub type TokenTree = ::tt::TokenTree; pub type Leaf = ::tt::Leaf; pub type Literal = ::tt::Literal; @@ -32,8 +27,10 @@ mod tt { type TokenStream = crate::server_impl::TokenStream; -#[derive(Clone)] -pub struct SourceFile; +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct SourceFile { + file_id: FileId, +} pub struct FreeFunctions; pub struct RaSpanServer { @@ -159,15 +156,8 @@ impl server::TokenStream for RaSpanServer { ) -> Self::TokenStream { match tree { bridge::TokenTree::Group(group) => { - let group = tt::Subtree { - delimiter: delim_to_internal(group.delimiter, group.span), - token_trees: match group.stream { - Some(stream) => stream.into_iter().collect(), - None => Box::new([]), - }, - }; - let tree = tt::TokenTree::from(group); - Self::TokenStream::from_iter(iter::once(tree)) + let group = TopSubtree::from_bridge(group); + TokenStream { token_trees: group.0 } } bridge::TokenTree::Ident(ident) => { @@ -179,7 +169,7 @@ impl server::TokenStream for RaSpanServer { }; let leaf = tt::Leaf::from(ident); let tree = tt::TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) + TokenStream { token_trees: vec![tree] } } bridge::TokenTree::Literal(literal) => { @@ -192,7 +182,7 @@ impl server::TokenStream for RaSpanServer { let leaf: tt::Leaf = tt::Leaf::from(literal); let tree = tt::TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) + TokenStream { token_trees: vec![tree] } } bridge::TokenTree::Punct(p) => { @@ -203,7 +193,7 @@ impl server::TokenStream for RaSpanServer { }; let leaf = tt::Leaf::from(punct); let tree = tt::TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) + TokenStream { token_trees: vec![tree] } } } } @@ -251,49 +241,13 @@ impl server::TokenStream for RaSpanServer { &mut self, stream: Self::TokenStream, ) -> Vec> { - stream - .into_iter() - .map(|tree| match tree { - tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { - bridge::TokenTree::Ident(bridge::Ident { - sym: ident.sym, - is_raw: ident.is_raw.yes(), - span: ident.span, - }) - } - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { - bridge::TokenTree::Literal(bridge::Literal { - span: lit.span, - kind: literal_kind_to_external(lit.kind), - symbol: lit.symbol, - suffix: lit.suffix, - }) - } - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { - bridge::TokenTree::Punct(bridge::Punct { - ch: punct.char as u8, - joint: punct.spacing == tt::Spacing::Joint, - span: punct.span, - }) - } - tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(bridge::Group { - delimiter: delim_to_external(subtree.delimiter), - stream: if subtree.token_trees.is_empty() { - None - } else { - Some(subtree.token_trees.into_vec().into_iter().collect()) - }, - span: bridge::DelimSpan::from_single(subtree.delimiter.open), - }), - }) - .collect() + stream.into_bridge() } } impl server::SourceFile for RaSpanServer { - fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool { - // FIXME - true + fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool { + file1 == file2 } fn path(&mut self, _file: &Self::SourceFile) -> String { // FIXME @@ -308,9 +262,8 @@ impl server::Span for RaSpanServer { fn debug(&mut self, span: Self::Span) -> String { format!("{:?}", span) } - fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile { - // FIXME stub, requires db - SourceFile {} + fn source_file(&mut self, span: Self::Span) -> Self::SourceFile { + SourceFile { file_id: span.anchor.file_id.file_id() } } fn save_span(&mut self, _span: Self::Span) -> usize { // FIXME, quote is incompatible with third-party tools @@ -507,13 +460,14 @@ mod tests { close: span, kind: tt::DelimiterKind::Brace, }, - token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { - kind: tt::LitKind::Str, - symbol: Symbol::intern("string"), - suffix: None, - span, - }))]), + len: 1, }), + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + kind: tt::LitKind::Str, + symbol: Symbol::intern("string"), + suffix: None, + span, + })), ], }; @@ -530,35 +484,38 @@ mod tests { }, ctx: SyntaxContextId::ROOT, }; - let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter { - open: span, - close: span, - kind: tt::DelimiterKind::Parenthesis, - }, - token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + let subtree_paren_a = vec![ + tt::TokenTree::Subtree(tt::Subtree { + delimiter: tt::Delimiter { + open: span, + close: span, + kind: tt::DelimiterKind::Parenthesis, + }, + len: 1, + }), + tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { is_raw: tt::IdentIsRaw::No, sym: Symbol::intern("a"), span, - }))]), - }); + })), + ]; let t1 = TokenStream::from_str("(a)", span).unwrap(); - assert_eq!(t1.token_trees.len(), 1); - assert_eq!(t1.token_trees[0], subtree_paren_a); + assert_eq!(t1.token_trees.len(), 2); + assert!(t1.token_trees == subtree_paren_a); let t2 = TokenStream::from_str("(a);", span).unwrap(); - assert_eq!(t2.token_trees.len(), 2); - assert_eq!(t2.token_trees[0], subtree_paren_a); + assert_eq!(t2.token_trees.len(), 3); + assert!(t2.token_trees[0..2] == subtree_paren_a); let underscore = TokenStream::from_str("_", span).unwrap(); - assert_eq!( - underscore.token_trees[0], - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - sym: Symbol::intern("_"), - span, - is_raw: tt::IdentIsRaw::No, - })) + assert!( + underscore.token_trees[0] + == tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + sym: Symbol::intern("_"), + span, + is_raw: tt::IdentIsRaw::No, + })) ); } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index e478b1c853be..466eb14b55ea 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -1,30 +1,22 @@ //! proc-macro server backend based on [`proc_macro_api::msg::TokenId`] as the backing span. //! This backend is rather inflexible, used by RustRover and older rust-analyzer versions. -use std::{ - iter, - ops::{Bound, Range}, -}; +use std::ops::{Bound, Range}; use intern::Symbol; use proc_macro::bridge::{self, server}; -use crate::server_impl::{ - delim_to_external, delim_to_internal, literal_kind_to_external, literal_kind_to_internal, - token_stream::TokenStreamBuilder, -}; +use crate::server_impl::{literal_kind_to_internal, token_stream::TokenStreamBuilder, TopSubtree}; mod tt { - pub use proc_macro_api::msg::TokenId; + pub use span::TokenId; pub use tt::*; - pub type Subtree = ::tt::Subtree; pub type TokenTree = ::tt::TokenTree; pub type Leaf = ::tt::Leaf; pub type Literal = ::tt::Literal; pub type Punct = ::tt::Punct; pub type Ident = ::tt::Ident; } -type Group = tt::Subtree; type TokenTree = tt::TokenTree; type Punct = tt::Punct; type Spacing = tt::Spacing; @@ -148,15 +140,8 @@ impl server::TokenStream for TokenIdServer { ) -> Self::TokenStream { match tree { bridge::TokenTree::Group(group) => { - let group = Group { - delimiter: delim_to_internal(group.delimiter, group.span), - token_trees: match group.stream { - Some(stream) => stream.into_iter().collect(), - None => Box::new([]), - }, - }; - let tree = TokenTree::from(group); - Self::TokenStream::from_iter(iter::once(tree)) + let group = TopSubtree::from_bridge(group); + TokenStream { token_trees: group.0 } } bridge::TokenTree::Ident(ident) => { @@ -167,7 +152,7 @@ impl server::TokenStream for TokenIdServer { }; let leaf = tt::Leaf::from(ident); let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) + TokenStream { token_trees: vec![tree] } } bridge::TokenTree::Literal(literal) => { @@ -180,7 +165,7 @@ impl server::TokenStream for TokenIdServer { let leaf = tt::Leaf::from(literal); let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) + TokenStream { token_trees: vec![tree] } } bridge::TokenTree::Punct(p) => { @@ -191,7 +176,7 @@ impl server::TokenStream for TokenIdServer { }; let leaf = tt::Leaf::from(punct); let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(iter::once(tree)) + TokenStream { token_trees: vec![tree] } } } } @@ -234,42 +219,7 @@ impl server::TokenStream for TokenIdServer { &mut self, stream: Self::TokenStream, ) -> Vec> { - stream - .into_iter() - .map(|tree| match tree { - tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { - bridge::TokenTree::Ident(bridge::Ident { - sym: ident.sym, - is_raw: ident.is_raw.yes(), - span: ident.span, - }) - } - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { - bridge::TokenTree::Literal(bridge::Literal { - span: lit.span, - kind: literal_kind_to_external(lit.kind), - symbol: lit.symbol, - suffix: lit.suffix, - }) - } - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { - bridge::TokenTree::Punct(bridge::Punct { - ch: punct.char as u8, - joint: punct.spacing == Spacing::Joint, - span: punct.span, - }) - } - tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(bridge::Group { - delimiter: delim_to_external(subtree.delimiter), - stream: if subtree.token_trees.is_empty() { - None - } else { - Some(TokenStream { token_trees: subtree.token_trees.into_vec() }) - }, - span: bridge::DelimSpan::from_single(subtree.delimiter.open), - }), - }) - .collect() + stream.into_bridge() } } @@ -398,7 +348,7 @@ mod tests { close: tt::TokenId(0), kind: tt::DelimiterKind::Brace, }, - token_trees: Box::new([]), + len: 0, }), ], }; @@ -408,35 +358,38 @@ mod tests { #[test] fn test_ra_server_from_str() { - let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter { - open: tt::TokenId(0), - close: tt::TokenId(0), - kind: tt::DelimiterKind::Parenthesis, - }, - token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + let subtree_paren_a = vec![ + tt::TokenTree::Subtree(tt::Subtree { + delimiter: tt::Delimiter { + open: tt::TokenId(0), + close: tt::TokenId(0), + kind: tt::DelimiterKind::Parenthesis, + }, + len: 1, + }), + tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { is_raw: tt::IdentIsRaw::No, sym: Symbol::intern("a"), span: tt::TokenId(0), - }))]), - }); + })), + ]; let t1 = TokenStream::from_str("(a)", tt::TokenId(0)).unwrap(); - assert_eq!(t1.token_trees.len(), 1); - assert_eq!(t1.token_trees[0], subtree_paren_a); + assert_eq!(t1.token_trees.len(), 2); + assert!(t1.token_trees[0..2] == subtree_paren_a); let t2 = TokenStream::from_str("(a);", tt::TokenId(0)).unwrap(); - assert_eq!(t2.token_trees.len(), 2); - assert_eq!(t2.token_trees[0], subtree_paren_a); + assert_eq!(t2.token_trees.len(), 3); + assert!(t2.token_trees[0..2] == subtree_paren_a); let underscore = TokenStream::from_str("_", tt::TokenId(0)).unwrap(); - assert_eq!( - underscore.token_trees[0], - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - sym: Symbol::intern("_"), - span: tt::TokenId(0), - is_raw: tt::IdentIsRaw::No, - })) + assert!( + underscore.token_trees[0] + == tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + sym: Symbol::intern("_"), + span: tt::TokenId(0), + is_raw: tt::IdentIsRaw::No, + })) ); } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs index 5649e60e0bb1..645f7e7c59a3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs @@ -1,10 +1,20 @@ //! TokenStream implementation used by sysroot ABI -use tt::TokenTree; +use proc_macro::bridge; -#[derive(Debug, Clone)] +use crate::server_impl::{delim_to_external, literal_kind_to_external, TopSubtree}; + +#[derive(Clone)] pub struct TokenStream { - pub(super) token_trees: Vec>, + pub(super) token_trees: Vec>, +} + +impl std::fmt::Debug for TokenStream { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TokenStream") + .field("token_trees", &tt::TokenTreesView::new(&self.token_trees)) + .finish() + } } impl Default for TokenStream { @@ -13,84 +23,85 @@ impl Default for TokenStream { } } -impl TokenStream { +impl TokenStream { pub(crate) fn new() -> Self { TokenStream::default() } - pub(crate) fn with_subtree(subtree: tt::Subtree) -> Self { - if subtree.delimiter.kind != tt::DelimiterKind::Invisible { - TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] } - } else { - TokenStream { token_trees: subtree.token_trees.into_vec() } + pub(crate) fn with_subtree(subtree: TopSubtree) -> Self { + let delimiter_kind = subtree.top_subtree().delimiter.kind; + let mut token_trees = subtree.0; + if delimiter_kind == tt::DelimiterKind::Invisible { + token_trees.remove(0); } + TokenStream { token_trees } } - pub(crate) fn into_subtree(self, call_site: S) -> tt::Subtree + pub(crate) fn into_subtree(mut self, call_site: S) -> TopSubtree where S: Copy, { - tt::Subtree { - delimiter: tt::Delimiter { - open: call_site, - close: call_site, - kind: tt::DelimiterKind::Invisible, - }, - token_trees: self.token_trees.into_boxed_slice(), - } + self.token_trees.insert( + 0, + tt::TokenTree::Subtree(tt::Subtree { + delimiter: tt::Delimiter { + open: call_site, + close: call_site, + kind: tt::DelimiterKind::Invisible, + }, + len: self.token_trees.len() as u32, + }), + ); + TopSubtree(self.token_trees) } pub(super) fn is_empty(&self) -> bool { self.token_trees.is_empty() } -} -/// Creates a token stream containing a single token tree. -impl From> for TokenStream { - fn from(tree: TokenTree) -> TokenStream { - TokenStream { token_trees: vec![tree] } - } -} - -/// Collects a number of token trees into a single stream. -impl FromIterator> for TokenStream { - fn from_iter>>(trees: I) -> Self { - trees.into_iter().map(TokenStream::from).collect() - } -} - -/// A "flattening" operation on token streams, collects token trees -/// from multiple token streams into a single stream. -impl FromIterator> for TokenStream { - fn from_iter>>(streams: I) -> Self { - let mut builder = TokenStreamBuilder::new(); - streams.into_iter().for_each(|stream| builder.push(stream)); - builder.build() - } -} - -impl Extend> for TokenStream { - fn extend>>(&mut self, trees: I) { - self.extend(trees.into_iter().map(TokenStream::from)); - } -} - -impl Extend> for TokenStream { - fn extend>>(&mut self, streams: I) { - for item in streams { - for tkn in item { - match tkn { - tt::TokenTree::Subtree(subtree) - if subtree.delimiter.kind == tt::DelimiterKind::Invisible => - { - self.token_trees.extend(subtree.token_trees.into_vec().into_iter()); - } - _ => { - self.token_trees.push(tkn); - } + pub(crate) fn into_bridge(self) -> Vec> { + let mut result = Vec::new(); + let mut iter = self.token_trees.into_iter(); + while let Some(tree) = iter.next() { + match tree { + tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { + result.push(bridge::TokenTree::Ident(bridge::Ident { + sym: ident.sym, + is_raw: ident.is_raw.yes(), + span: ident.span, + })) + } + tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { + result.push(bridge::TokenTree::Literal(bridge::Literal { + span: lit.span, + kind: literal_kind_to_external(lit.kind), + symbol: lit.symbol, + suffix: lit.suffix, + })) + } + tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { + result.push(bridge::TokenTree::Punct(bridge::Punct { + ch: punct.char as u8, + joint: punct.spacing == tt::Spacing::Joint, + span: punct.span, + })) + } + tt::TokenTree::Subtree(subtree) => { + result.push(bridge::TokenTree::Group(bridge::Group { + delimiter: delim_to_external(subtree.delimiter), + stream: if subtree.len == 0 { + None + } else { + Some(TokenStream { + token_trees: iter.by_ref().take(subtree.usize_len()).collect(), + }) + }, + span: bridge::DelimSpan::from_single(subtree.delimiter.open), + })) } } } + result } } @@ -103,19 +114,7 @@ pub(super) mod token_stream_impls { use core::fmt; - use super::{TokenStream, TokenTree}; - - /// An iterator over `TokenStream`'s `TokenTree`s. - /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, - /// and returns whole groups as token trees. - impl IntoIterator for TokenStream { - type Item = TokenTree; - type IntoIter = std::vec::IntoIter>; - - fn into_iter(self) -> Self::IntoIter { - self.token_trees.into_iter() - } - } + use super::{TokenStream, TopSubtree}; /// Attempts to break the string into tokens and parse those tokens into a token stream. /// May fail for a number of reasons, for example, if the string contains unbalanced delimiters @@ -133,7 +132,7 @@ pub(super) mod token_stream_impls { ) .ok_or_else(|| format!("lexing error: {src}"))?; - Ok(TokenStream::with_subtree(subtree)) + Ok(TokenStream::with_subtree(TopSubtree(subtree.0.into_vec()))) } } @@ -145,13 +144,13 @@ pub(super) mod token_stream_impls { } } -impl TokenStreamBuilder { +impl TokenStreamBuilder { pub(super) fn new() -> TokenStreamBuilder { TokenStreamBuilder { acc: TokenStream::new() } } pub(super) fn push(&mut self, stream: TokenStream) { - self.acc.extend(stream) + self.acc.token_trees.extend(stream.token_trees) } pub(super) fn build(self) -> TokenStream { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index cc5d4a891318..37d51050f32c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -1,17 +1,18 @@ //! utils used in proc-macro tests use expect_test::Expect; -use proc_macro_api::msg::TokenId; -use span::{EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId}; +use span::{EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId, TokenId}; use tt::TextRange; use crate::{dylib, proc_macro_test_dylib_path, EnvSnapshot, ProcMacroSrv}; fn parse_string(call_site: TokenId, src: &str) -> crate::server_impl::TokenStream { - crate::server_impl::TokenStream::with_subtree( + crate::server_impl::TokenStream::with_subtree(crate::server_impl::TopSubtree( syntax_bridge::parse_to_token_tree_static_span(span::Edition::CURRENT, call_site, src) - .unwrap(), - ) + .unwrap() + .0 + .into_vec(), + )) } fn parse_string_spanned( @@ -19,9 +20,12 @@ fn parse_string_spanned( call_site: SyntaxContextId, src: &str, ) -> crate::server_impl::TokenStream { - crate::server_impl::TokenStream::with_subtree( - syntax_bridge::parse_to_token_tree(span::Edition::CURRENT, anchor, call_site, src).unwrap(), - ) + crate::server_impl::TokenStream::with_subtree(crate::server_impl::TopSubtree( + syntax_bridge::parse_to_token_tree(span::Edition::CURRENT, anchor, call_site, src) + .unwrap() + .0 + .into_vec(), + )) } pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect, expect_s: Expect) { diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index 524323b97363..b0939229f93e 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -172,19 +172,18 @@ impl WorkspaceBuildScripts { } let res = (|| { let target_libdir = (|| { - let mut cargo_config = sysroot.tool(Tool::Cargo); + let mut cargo_config = sysroot.tool(Tool::Cargo, current_dir); cargo_config.envs(extra_env); cargo_config - .current_dir(current_dir) .args(["rustc", "-Z", "unstable-options", "--print", "target-libdir"]) .env("RUSTC_BOOTSTRAP", "1"); - if let Ok(it) = utf8_stdout(cargo_config) { + if let Ok(it) = utf8_stdout(&mut cargo_config) { return Ok(it); } - let mut cmd = sysroot.tool(Tool::Rustc); + let mut cmd = sysroot.tool(Tool::Rustc, current_dir); cmd.envs(extra_env); cmd.args(["--print", "target-libdir"]); - utf8_stdout(cmd) + utf8_stdout(&mut cmd) })()?; let target_libdir = AbsPathBuf::try_from(Utf8PathBuf::from(target_libdir)) @@ -390,12 +389,12 @@ impl WorkspaceBuildScripts { ) -> io::Result { let mut cmd = match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { - let mut cmd = Command::new(program); + let mut cmd = toolchain::command(program, current_dir); cmd.args(args); cmd } _ => { - let mut cmd = sysroot.tool(Tool::Cargo); + let mut cmd = sysroot.tool(Tool::Cargo, current_dir); cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]); cmd.args(&config.extra_args); @@ -448,7 +447,6 @@ impl WorkspaceBuildScripts { } }; - cmd.current_dir(current_dir); cmd.envs(&config.extra_env); if config.wrap_rustc_in_build_scripts { // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index ba4946bf0b99..4d906c2aeb3b 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -4,6 +4,7 @@ use std::ops; use std::str::from_utf8; use anyhow::Context; +use base_db::Env; use cargo_metadata::{CargoOpt, MetadataCommand}; use la_arena::{Arena, Idx}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; @@ -13,8 +14,8 @@ use serde_json::from_value; use span::Edition; use toolchain::Tool; -use crate::{utf8_stdout, ManifestPath, Sysroot, SysrootQueryMetadata}; use crate::{CfgOverrides, InvocationStrategy}; +use crate::{ManifestPath, Sysroot}; /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo /// workspace. It pretty closely mirrors `cargo metadata` output. @@ -34,6 +35,8 @@ pub struct CargoWorkspace { target_directory: AbsPathBuf, manifest_path: ManifestPath, is_virtual_workspace: bool, + /// Environment variables set in the `.cargo/config` file. + config_env: Env, } impl ops::Index for CargoWorkspace { @@ -86,8 +89,6 @@ pub struct CargoConfig { pub target: Option, /// Sysroot loading behavior pub sysroot: Option, - /// How to query metadata for the sysroot crate. - pub sysroot_query_metadata: SysrootQueryMetadata, pub sysroot_src: Option, /// rustc private crate source pub rustc_source: Option, @@ -251,6 +252,18 @@ impl TargetKind { } } +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub struct CargoMetadataConfig { + /// List of features to activate. + pub features: CargoFeatures, + /// rustc targets + pub targets: Vec, + /// Extra args to pass to the cargo command. + pub extra_args: Vec, + /// Extra env vars to set when invoking the cargo command + pub extra_env: FxHashMap, +} + // Deserialize helper for the cargo metadata #[derive(Deserialize, Default)] struct PackageMetadata { @@ -265,7 +278,7 @@ impl CargoWorkspace { pub fn fetch_metadata( cargo_toml: &ManifestPath, current_dir: &AbsPath, - config: &CargoConfig, + config: &CargoMetadataConfig, sysroot: &Sysroot, locked: bool, progress: &dyn Fn(String), @@ -276,15 +289,13 @@ impl CargoWorkspace { fn fetch_metadata_( cargo_toml: &ManifestPath, current_dir: &AbsPath, - config: &CargoConfig, + config: &CargoMetadataConfig, sysroot: &Sysroot, locked: bool, no_deps: bool, progress: &dyn Fn(String), ) -> anyhow::Result<(cargo_metadata::Metadata, Option)> { - let targets = find_list_of_build_targets(config, cargo_toml, sysroot); - - let cargo = sysroot.tool(Tool::Cargo); + let cargo = sysroot.tool(Tool::Cargo, current_dir); let mut meta = MetadataCommand::new(); meta.cargo_path(cargo.get_program()); cargo.get_envs().for_each(|(var, val)| _ = meta.env(var, val.unwrap_or_default())); @@ -319,12 +330,9 @@ impl CargoWorkspace { } } - if !targets.is_empty() { - other_options.append( - &mut targets - .into_iter() - .flat_map(|target| ["--filter-platform".to_owned(), target]) - .collect(), + if !config.targets.is_empty() { + other_options.extend( + config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]), ); } // The manifest is a rust file, so this means its a script manifest @@ -388,6 +396,7 @@ impl CargoWorkspace { pub fn new( mut meta: cargo_metadata::Metadata, ws_manifest_path: ManifestPath, + cargo_config_env: Env, ) -> CargoWorkspace { let mut pkg_by_id = FxHashMap::default(); let mut packages = Arena::default(); @@ -509,6 +518,7 @@ impl CargoWorkspace { target_directory, manifest_path: ws_manifest_path, is_virtual_workspace, + config_env: cargo_config_env, } } @@ -595,80 +605,8 @@ impl CargoWorkspace { pub fn is_virtual_workspace(&self) -> bool { self.is_virtual_workspace } -} -fn find_list_of_build_targets( - config: &CargoConfig, - cargo_toml: &ManifestPath, - sysroot: &Sysroot, -) -> Vec { - if let Some(target) = &config.target { - return [target.into()].to_vec(); - } - - let build_targets = cargo_config_build_target(cargo_toml, &config.extra_env, sysroot); - if !build_targets.is_empty() { - return build_targets; - } - - rustc_discover_host_triple(cargo_toml, &config.extra_env, sysroot).into_iter().collect() -} - -fn rustc_discover_host_triple( - cargo_toml: &ManifestPath, - extra_env: &FxHashMap, - sysroot: &Sysroot, -) -> Option { - let mut rustc = sysroot.tool(Tool::Rustc); - rustc.envs(extra_env); - rustc.current_dir(cargo_toml.parent()).arg("-vV"); - tracing::debug!("Discovering host platform by {:?}", rustc); - match utf8_stdout(rustc) { - Ok(stdout) => { - let field = "host: "; - let target = stdout.lines().find_map(|l| l.strip_prefix(field)); - if let Some(target) = target { - Some(target.to_owned()) - } else { - // If we fail to resolve the host platform, it's not the end of the world. - tracing::info!("rustc -vV did not report host platform, got:\n{}", stdout); - None - } - } - Err(e) => { - tracing::warn!("Failed to discover host platform: {}", e); - None - } + pub fn env(&self) -> &Env { + &self.config_env } } - -fn cargo_config_build_target( - cargo_toml: &ManifestPath, - extra_env: &FxHashMap, - sysroot: &Sysroot, -) -> Vec { - let mut cargo_config = sysroot.tool(Tool::Cargo); - cargo_config.envs(extra_env); - cargo_config - .current_dir(cargo_toml.parent()) - .args(["-Z", "unstable-options", "config", "get", "build.target"]) - .env("RUSTC_BOOTSTRAP", "1"); - // if successful we receive `build.target = "target-triple"` - // or `build.target = ["", ..]` - tracing::debug!("Discovering cargo config target by {:?}", cargo_config); - utf8_stdout(cargo_config).map(parse_output_cargo_config_build_target).unwrap_or_default() -} - -fn parse_output_cargo_config_build_target(stdout: String) -> Vec { - let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"'); - - if !trimmed.starts_with('[') { - return [trimmed.to_owned()].to_vec(); - } - - let res = serde_json::from_str(trimmed); - if let Err(e) = &res { - tracing::warn!("Failed to parse `build.target` as an array of target: {}`", e); - } - res.unwrap_or_default() -} diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs index ff9d2035f60a..37fffba29559 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/env.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs @@ -1,9 +1,10 @@ //! Cargo-like environment variables injection. use base_db::Env; +use paths::Utf8Path; use rustc_hash::FxHashMap; use toolchain::Tool; -use crate::{utf8_stdout, CargoWorkspace, ManifestPath, PackageData, Sysroot, TargetKind}; +use crate::{utf8_stdout, ManifestPath, PackageData, Sysroot, TargetKind}; /// Recreates the compile-time environment variables that Cargo sets. /// @@ -50,34 +51,23 @@ pub(crate) fn inject_cargo_env(env: &mut Env) { env.set("CARGO", Tool::Cargo.path().to_string()); } -pub(crate) fn inject_rustc_tool_env( - env: &mut Env, - cargo: &CargoWorkspace, - cargo_name: &str, - kind: TargetKind, -) { +pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: TargetKind) { _ = kind; // FIXME // if kind.is_executable() { // env.set("CARGO_BIN_NAME", cargo_name); // } env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_")); - // NOTE: Technically we should set this for all crates, but that will worsen the deduplication - // logic so for now just keeping it proc-macros ought to be fine. - if kind.is_proc_macro() { - env.set("CARGO_RUSTC_CURRENT_DIR", cargo.manifest_path().parent().to_string()); - } } pub(crate) fn cargo_config_env( manifest: &ManifestPath, extra_env: &FxHashMap, sysroot: &Sysroot, -) -> FxHashMap { - let mut cargo_config = sysroot.tool(Tool::Cargo); +) -> Env { + let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent()); cargo_config.envs(extra_env); cargo_config - .current_dir(manifest.parent()) .args(["-Z", "unstable-options", "config", "get", "env"]) .env("RUSTC_BOOTSTRAP", "1"); if manifest.is_rust_manifest() { @@ -85,8 +75,8 @@ pub(crate) fn cargo_config_env( } // if successful we receive `env.key.value = "value" per entry tracing::debug!("Discovering cargo config env by {:?}", cargo_config); - utf8_stdout(cargo_config) - .map(parse_output_cargo_config_env) + utf8_stdout(&mut cargo_config) + .map(|stdout| parse_output_cargo_config_env(manifest, &stdout)) .inspect(|env| { tracing::debug!("Discovered cargo config env: {:?}", env); }) @@ -96,18 +86,61 @@ pub(crate) fn cargo_config_env( .unwrap_or_default() } -fn parse_output_cargo_config_env(stdout: String) -> FxHashMap { - stdout - .lines() - .filter_map(|l| l.strip_prefix("env.")) - .filter_map(|l| l.split_once(" = ")) - .filter_map(|(k, v)| { - if k.contains('.') { - k.strip_suffix(".value").zip(Some(v)) - } else { - Some((k, v)) +fn parse_output_cargo_config_env(manifest: &ManifestPath, stdout: &str) -> Env { + let mut env = Env::default(); + let mut relatives = vec![]; + for (key, val) in + stdout.lines().filter_map(|l| l.strip_prefix("env.")).filter_map(|l| l.split_once(" = ")) + { + let val = val.trim_matches('"').to_owned(); + if let Some((key, modifier)) = key.split_once('.') { + match modifier { + "relative" => relatives.push((key, val)), + "value" => _ = env.insert(key, val), + _ => { + tracing::warn!( + "Unknown modifier in cargo config env: {}, expected `relative` or `value`", + modifier + ); + continue; + } } - }) - .map(|(key, value)| (key.to_owned(), value.trim_matches('"').to_owned())) - .collect() + } else { + env.insert(key, val); + } + } + // FIXME: The base here should be the parent of the `.cargo/config` file, not the manifest. + // But cargo does not provide this information. + let base = <_ as AsRef>::as_ref(manifest.parent()); + for (key, relative) in relatives { + if relative != "true" { + continue; + } + if let Some(suffix) = env.get(key) { + env.insert(key, base.join(suffix).to_string()); + } + } + env +} + +#[test] +fn parse_output_cargo_config_env_works() { + let stdout = r#" +env.CARGO_WORKSPACE_DIR.relative = true +env.CARGO_WORKSPACE_DIR.value = "" +env.RELATIVE.relative = true +env.RELATIVE.value = "../relative" +env.INVALID.relative = invalidbool +env.INVALID.value = "../relative" +env.TEST.value = "test" +"# + .trim(); + let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap(); + let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml")); + let manifest = ManifestPath::try_from(manifest).unwrap(); + let env = parse_output_cargo_config_env(&manifest, stdout); + assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str())); + assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str())); + assert_eq!(env.get("INVALID").as_deref(), Some("../relative")); + assert_eq!(env.get("TEST").as_deref(), Some("test")); } diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index da8afc5d3a18..fc1fd7b877fc 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -15,14 +15,32 @@ //! procedural macros). //! * Lowering of concrete model to a [`base_db::CrateGraph`] +pub mod project_json; +pub mod toolchain_info { + pub mod rustc_cfg; + pub mod target_data_layout; + pub mod target_tuple; + pub mod version; + + use std::path::Path; + + use crate::{ManifestPath, Sysroot}; + + #[derive(Copy, Clone)] + pub enum QueryConfig<'a> { + /// Directly invoke `rustc` to query the desired information. + Rustc(&'a Sysroot, &'a Path), + /// Attempt to use cargo to query the desired information, honoring cargo configurations. + /// If this fails, falls back to invoking `rustc` directly. + Cargo(&'a Sysroot, &'a ManifestPath), + } +} + mod build_dependencies; mod cargo_workspace; mod env; mod manifest_path; -pub mod project_json; -mod rustc_cfg; mod sysroot; -pub mod target_data_layout; mod workspace; #[cfg(test)] @@ -42,8 +60,8 @@ use rustc_hash::FxHashSet; pub use crate::{ build_dependencies::WorkspaceBuildScripts, cargo_workspace::{ - CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency, - RustLibSource, Target, TargetData, TargetKind, + CargoConfig, CargoFeatures, CargoMetadataConfig, CargoWorkspace, Package, PackageData, + PackageDependency, RustLibSource, Target, TargetData, TargetKind, }, manifest_path::ManifestPath, project_json::{ProjectJson, ProjectJsonData}, @@ -181,7 +199,7 @@ impl fmt::Display for ProjectManifest { } } -fn utf8_stdout(mut cmd: Command) -> anyhow::Result { +fn utf8_stdout(cmd: &mut Command) -> anyhow::Result { let output = cmd.output().with_context(|| format!("{cmd:?} failed"))?; if !output.status.success() { match String::from_utf8(output.stderr) { @@ -241,9 +259,20 @@ fn parse_cfg(s: &str) -> Result { Ok(res) } -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum SysrootQueryMetadata { - #[default] - CargoMetadata, - None, +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SysrootSourceWorkspaceConfig { + CargoMetadata(CargoMetadataConfig), + Stitched, +} + +impl Default for SysrootSourceWorkspaceConfig { + fn default() -> Self { + SysrootSourceWorkspaceConfig::default_cargo() + } +} + +impl SysrootSourceWorkspaceConfig { + pub fn default_cargo() -> Self { + SysrootSourceWorkspaceConfig::CargoMetadata(Default::default()) + } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs index a72dab607525..73a4e6e12169 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs @@ -46,7 +46,7 @@ impl ManifestPath { } pub fn is_rust_manifest(&self) -> bool { - self.file.extension().map_or(false, |ext| ext == "rs") + self.file.extension() == Some("rs") } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs deleted file mode 100644 index bc1f0e6fbf26..000000000000 --- a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! Runs `rustc --print cfg` to get built-in cfg flags. - -use anyhow::Context; -use cfg::CfgAtom; -use intern::Symbol; -use rustc_hash::FxHashMap; -use toolchain::Tool; - -use crate::{utf8_stdout, ManifestPath, Sysroot}; - -/// Determines how `rustc --print cfg` is discovered and invoked. -pub(crate) enum RustcCfgConfig<'a> { - /// Use `rustc --print cfg`, either from with the binary from the sysroot or by discovering via - /// [`toolchain::rustc`]. - Rustc(&'a Sysroot), - /// Use `cargo --print cfg`, either from with the binary from the sysroot or by discovering via - /// [`toolchain::cargo`]. - Cargo(&'a Sysroot, &'a ManifestPath), -} - -pub(crate) fn get( - target: Option<&str>, - extra_env: &FxHashMap, - config: RustcCfgConfig<'_>, -) -> Vec { - let _p = tracing::info_span!("rustc_cfg::get").entered(); - let mut res: Vec<_> = Vec::with_capacity(7 * 2 + 1); - - // Some nightly-only cfgs, which are required for stdlib - res.push(CfgAtom::Flag(Symbol::intern("target_thread_local"))); - for key in ["target_has_atomic", "target_has_atomic_load_store"] { - for ty in ["8", "16", "32", "64", "cas", "ptr"] { - res.push(CfgAtom::KeyValue { key: Symbol::intern(key), value: Symbol::intern(ty) }); - } - res.push(CfgAtom::Flag(Symbol::intern(key))); - } - - let rustc_cfgs = get_rust_cfgs(target, extra_env, config); - - let rustc_cfgs = match rustc_cfgs { - Ok(cfgs) => cfgs, - Err(e) => { - tracing::error!(?e, "failed to get rustc cfgs"); - return res; - } - }; - - let rustc_cfgs = rustc_cfgs.lines().map(crate::parse_cfg).collect::, _>>(); - - match rustc_cfgs { - Ok(rustc_cfgs) => { - tracing::debug!(?rustc_cfgs, "rustc cfgs found"); - res.extend(rustc_cfgs); - } - Err(e) => { - tracing::error!(?e, "failed to get rustc cfgs") - } - } - - res -} - -fn get_rust_cfgs( - target: Option<&str>, - extra_env: &FxHashMap, - config: RustcCfgConfig<'_>, -) -> anyhow::Result { - let sysroot = match config { - RustcCfgConfig::Cargo(sysroot, cargo_toml) => { - let mut cmd = sysroot.tool(Tool::Cargo); - - cmd.envs(extra_env); - cmd.current_dir(cargo_toml.parent()) - .args(["rustc", "-Z", "unstable-options", "--print", "cfg"]) - .env("RUSTC_BOOTSTRAP", "1"); - if let Some(target) = target { - cmd.args(["--target", target]); - } - - match utf8_stdout(cmd) { - Ok(it) => return Ok(it), - Err(e) => { - tracing::warn!("failed to run `cargo rustc --print cfg`, falling back to invoking rustc directly: {e}"); - sysroot - } - } - } - RustcCfgConfig::Rustc(sysroot) => sysroot, - }; - - let mut cmd = sysroot.tool(Tool::Rustc); - cmd.envs(extra_env); - cmd.args(["--print", "cfg", "-O"]); - if let Some(target) = target { - cmd.args(["--target", target]); - } - - utf8_stdout(cmd).context("unable to fetch cfgs via `rustc --print cfg -O`") -} diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index 8426e689a64d..8f633d24be9a 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -4,7 +4,12 @@ //! but we can't process `.rlib` and need source code instead. The source code //! is typically installed with `rustup component add rust-src` command. -use std::{env, fs, ops, process::Command}; +use std::{ + env, fs, + ops::{self, Not}, + path::Path, + process::Command, +}; use anyhow::{format_err, Result}; use base_db::CrateName; @@ -12,20 +17,24 @@ use itertools::Itertools; use la_arena::{Arena, Idx}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; +use stdx::format_to; use toolchain::{probe_for_binary, Tool}; -use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, SysrootQueryMetadata}; +use crate::{ + cargo_workspace::CargoMetadataConfig, utf8_stdout, CargoWorkspace, ManifestPath, + SysrootSourceWorkspaceConfig, +}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Sysroot { root: Option, src_root: Option, - mode: SysrootMode, + workspace: SysrootWorkspace, error: Option, } #[derive(Debug, Clone, Eq, PartialEq)] -pub(crate) enum SysrootMode { +pub(crate) enum SysrootWorkspace { Workspace(CargoWorkspace), Stitched(Stitched), Empty, @@ -79,7 +88,7 @@ pub(crate) struct SysrootCrateData { impl Sysroot { pub const fn empty() -> Sysroot { - Sysroot { root: None, src_root: None, mode: SysrootMode::Empty, error: None } + Sysroot { root: None, src_root: None, workspace: SysrootWorkspace::Empty, error: None } } /// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/` @@ -96,10 +105,10 @@ impl Sysroot { } pub fn is_empty(&self) -> bool { - match &self.mode { - SysrootMode::Workspace(ws) => ws.packages().next().is_none(), - SysrootMode::Stitched(stitched) => stitched.crates.is_empty(), - SysrootMode::Empty => true, + match &self.workspace { + SysrootWorkspace::Workspace(ws) => ws.packages().next().is_none(), + SysrootWorkspace::Stitched(stitched) => stitched.crates.is_empty(), + SysrootWorkspace::Empty => true, } } @@ -108,66 +117,53 @@ impl Sysroot { } pub fn num_packages(&self) -> usize { - match &self.mode { - SysrootMode::Workspace(ws) => ws.packages().count(), - SysrootMode::Stitched(c) => c.crates().count(), - SysrootMode::Empty => 0, + match &self.workspace { + SysrootWorkspace::Workspace(ws) => ws.packages().count(), + SysrootWorkspace::Stitched(c) => c.crates().count(), + SysrootWorkspace::Empty => 0, } } - pub(crate) fn mode(&self) -> &SysrootMode { - &self.mode + pub(crate) fn workspace(&self) -> &SysrootWorkspace { + &self.workspace } } -// FIXME: Expose a builder api as loading the sysroot got way too modular and complicated. impl Sysroot { /// Attempts to discover the toolchain's sysroot from the given `dir`. - pub fn discover( - dir: &AbsPath, - extra_env: &FxHashMap, - sysroot_query_metadata: SysrootQueryMetadata, - ) -> Sysroot { + pub fn discover(dir: &AbsPath, extra_env: &FxHashMap) -> Sysroot { let sysroot_dir = discover_sysroot_dir(dir, extra_env); let sysroot_src_dir = sysroot_dir.as_ref().ok().map(|sysroot_dir| { discover_sysroot_src_dir_or_add_component(sysroot_dir, dir, extra_env) }); - Sysroot::load_core_check(Some(sysroot_dir), sysroot_src_dir, sysroot_query_metadata) + Sysroot::assemble(Some(sysroot_dir), sysroot_src_dir) } pub fn discover_with_src_override( current_dir: &AbsPath, extra_env: &FxHashMap, sysroot_src_dir: AbsPathBuf, - sysroot_query_metadata: SysrootQueryMetadata, ) -> Sysroot { let sysroot_dir = discover_sysroot_dir(current_dir, extra_env); - Sysroot::load_core_check( - Some(sysroot_dir), - Some(Ok(sysroot_src_dir)), - sysroot_query_metadata, - ) + Sysroot::assemble(Some(sysroot_dir), Some(Ok(sysroot_src_dir))) } - pub fn discover_sysroot_src_dir( - sysroot_dir: AbsPathBuf, - sysroot_query_metadata: SysrootQueryMetadata, - ) -> Sysroot { + pub fn discover_sysroot_src_dir(sysroot_dir: AbsPathBuf) -> Sysroot { let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir) .ok_or_else(|| format_err!("can't find standard library sources in {sysroot_dir}")); - Sysroot::load_core_check( - Some(Ok(sysroot_dir)), - Some(sysroot_src_dir), - sysroot_query_metadata, - ) + Sysroot::assemble(Some(Ok(sysroot_dir)), Some(sysroot_src_dir)) } pub fn discover_rustc_src(&self) -> Option { get_rustc_src(self.root()?) } + pub fn new(sysroot_dir: Option, sysroot_src_dir: Option) -> Sysroot { + Self::assemble(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok)) + } + /// Returns a command to run a tool preferring the cargo proxies if the sysroot exists. - pub fn tool(&self, tool: Tool) -> Command { + pub fn tool(&self, tool: Tool, current_dir: impl AsRef) -> Command { match self.root() { Some(root) => { // special case rustc, we can look that up directly in the sysroot's bin folder @@ -176,15 +172,15 @@ impl Sysroot { if let Some(path) = probe_for_binary(root.join("bin").join(Tool::Rustc.name()).into()) { - return Command::new(path); + return toolchain::command(path, current_dir); } } - let mut cmd = Command::new(tool.prefer_proxy()); + let mut cmd = toolchain::command(tool.prefer_proxy(), current_dir); cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(root)); cmd } - _ => Command::new(tool.path()), + _ => toolchain::command(tool.path(), current_dir), } } @@ -202,90 +198,51 @@ impl Sysroot { }) } - pub fn load( - sysroot_dir: Option, - sysroot_src_dir: Option, - sysroot_query_metadata: SysrootQueryMetadata, - ) -> Sysroot { - Self::load_core_check(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok), sysroot_query_metadata) - } - - fn load_core_check( + fn assemble( sysroot_dir: Option>, sysroot_src_dir: Option>, - sysroot_query_metadata: SysrootQueryMetadata, ) -> Sysroot { - let mut sysroot = Self::load_(sysroot_dir, sysroot_src_dir, sysroot_query_metadata); - if sysroot.error.is_none() { - if let Some(src_root) = &sysroot.src_root { - let has_core = match &sysroot.mode { - SysrootMode::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"), - SysrootMode::Stitched(stitched) => stitched.by_name("core").is_some(), - SysrootMode::Empty => true, - }; - if !has_core { - let var_note = if env::var_os("RUST_SRC_PATH").is_some() { - " (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)" - } else { - ", try running `rustup component add rust-src` to possibly fix this" - }; - sysroot.error = Some(format!( - "sysroot at `{src_root}` is missing a `core` library{var_note}", - )); - } - } - } - sysroot - } - - fn load_( - sysroot_dir: Option>, - sysroot_src_dir: Option>, - sysroot_query_metadata: SysrootQueryMetadata, - ) -> Sysroot { - let sysroot_dir = match sysroot_dir { + let mut errors = String::new(); + let root = match sysroot_dir { Some(Ok(sysroot_dir)) => Some(sysroot_dir), Some(Err(e)) => { - return Sysroot { - root: None, - src_root: None, - mode: SysrootMode::Empty, - error: Some(e.to_string()), - } + format_to!(errors, "{e}\n"); + None } None => None, }; - let sysroot_src_dir = match sysroot_src_dir { - Some(Ok(sysroot_src_dir)) => sysroot_src_dir, + let src_root = match sysroot_src_dir { + Some(Ok(sysroot_src_dir)) => Some(sysroot_src_dir), Some(Err(e)) => { - return Sysroot { - root: sysroot_dir, - src_root: None, - mode: SysrootMode::Empty, - error: Some(e.to_string()), - } - } - None => { - return Sysroot { - root: sysroot_dir, - src_root: None, - mode: SysrootMode::Empty, - error: None, - } + format_to!(errors, "{e}\n"); + None } + None => None, }; - if sysroot_query_metadata == SysrootQueryMetadata::CargoMetadata { - let library_manifest = - ManifestPath::try_from(sysroot_src_dir.join("Cargo.toml")).unwrap(); + Sysroot { + root, + src_root, + workspace: SysrootWorkspace::Empty, + error: errors.is_empty().not().then_some(errors), + } + } + + pub fn load_workspace(&mut self, sysroot_source_config: &SysrootSourceWorkspaceConfig) { + assert!(matches!(self.workspace, SysrootWorkspace::Empty), "workspace already loaded"); + let Self { root: _, src_root: Some(src_root), workspace, error: _ } = self else { return }; + if let SysrootSourceWorkspaceConfig::CargoMetadata(cargo_config) = sysroot_source_config { + let library_manifest = ManifestPath::try_from(src_root.join("Cargo.toml")).unwrap(); if fs::metadata(&library_manifest).is_ok() { - if let Some(sysroot) = - Self::load_library_via_cargo(library_manifest, &sysroot_dir, &sysroot_src_dir) + if let Some(loaded) = + Self::load_library_via_cargo(library_manifest, src_root, cargo_config) { - return sysroot; + *workspace = loaded; + self.load_core_check(); + return; } } } - tracing::debug!("Stitching sysroot library: {sysroot_src_dir}"); + tracing::debug!("Stitching sysroot library: {src_root}"); let mut stitched = Stitched { crates: Arena::default() }; @@ -293,7 +250,7 @@ impl Sysroot { let name = path.split('/').last().unwrap(); let root = [format!("{path}/src/lib.rs"), format!("lib{path}/lib.rs")] .into_iter() - .map(|it| sysroot_src_dir.join(it)) + .map(|it| src_root.join(it)) .filter_map(|it| ManifestPath::try_from(it).ok()) .find(|it| fs::metadata(it).is_ok()); @@ -329,21 +286,39 @@ impl Sysroot { } } } - Sysroot { - root: sysroot_dir, - src_root: Some(sysroot_src_dir), - mode: SysrootMode::Stitched(stitched), - error: None, + *workspace = SysrootWorkspace::Stitched(stitched); + self.load_core_check(); + } + + fn load_core_check(&mut self) { + if self.error.is_none() { + if let Some(src_root) = &self.src_root { + let has_core = match &self.workspace { + SysrootWorkspace::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"), + SysrootWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(), + SysrootWorkspace::Empty => true, + }; + if !has_core { + let var_note = if env::var_os("RUST_SRC_PATH").is_some() { + " (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)" + } else { + ", try running `rustup component add rust-src` to possibly fix this" + }; + self.error = Some(format!( + "sysroot at `{src_root}` is missing a `core` library{var_note}", + )); + } + } } } fn load_library_via_cargo( library_manifest: ManifestPath, - sysroot_dir: &Option, sysroot_src_dir: &AbsPathBuf, - ) -> Option { + cargo_config: &CargoMetadataConfig, + ) -> Option { tracing::debug!("Loading library metadata: {library_manifest}"); - let mut cargo_config = CargoConfig::default(); + let mut cargo_config = cargo_config.clone(); // the sysroot uses `public-dependency`, so we make cargo think it's a nightly cargo_config.extra_env.insert( "__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS".to_owned(), @@ -415,13 +390,8 @@ impl Sysroot { res.packages.remove(idx); }); - let cargo_workspace = CargoWorkspace::new(res, library_manifest); - Some(Sysroot { - root: sysroot_dir.clone(), - src_root: Some(sysroot_src_dir.clone()), - mode: SysrootMode::Workspace(cargo_workspace), - error: None, - }) + let cargo_workspace = CargoWorkspace::new(res, library_manifest, Default::default()); + Some(SysrootWorkspace::Workspace(cargo_workspace)) } } @@ -429,11 +399,11 @@ fn discover_sysroot_dir( current_dir: &AbsPath, extra_env: &FxHashMap, ) -> Result { - let mut rustc = Command::new(Tool::Rustc.path()); + let mut rustc = toolchain::command(Tool::Rustc.path(), current_dir); rustc.envs(extra_env); rustc.current_dir(current_dir).args(["--print", "sysroot"]); tracing::debug!("Discovering sysroot by {:?}", rustc); - let stdout = utf8_stdout(rustc)?; + let stdout = utf8_stdout(&mut rustc)?; Ok(AbsPathBuf::assert(Utf8PathBuf::from(stdout))) } @@ -461,11 +431,11 @@ fn discover_sysroot_src_dir_or_add_component( ) -> Result { discover_sysroot_src_dir(sysroot_path) .or_else(|| { - let mut rustup = Command::new(Tool::Rustup.prefer_proxy()); + let mut rustup = toolchain::command(Tool::Rustup.prefer_proxy(), current_dir); rustup.envs(extra_env); - rustup.current_dir(current_dir).args(["component", "add", "rust-src"]); + rustup.args(["component", "add", "rust-src"]); tracing::info!("adding rust-src component by {:?}", rustup); - utf8_stdout(rustup).ok()?; + utf8_stdout(&mut rustup).ok()?; get_rust_src(sysroot_path) }) .ok_or_else(|| { diff --git a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs deleted file mode 100644 index 8a8a2d32558b..000000000000 --- a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! Runs `rustc --print target-spec-json` to get the target_data_layout. - -use rustc_hash::FxHashMap; -use toolchain::Tool; - -use crate::{utf8_stdout, ManifestPath, Sysroot}; - -/// Determines how `rustc --print target-spec-json` is discovered and invoked. -pub enum RustcDataLayoutConfig<'a> { - /// Use `rustc --print target-spec-json`, either from with the binary from the sysroot or by discovering via - /// [`toolchain::rustc`]. - Rustc(&'a Sysroot), - /// Use `cargo --print target-spec-json`, either from with the binary from the sysroot or by discovering via - /// [`toolchain::cargo`]. - Cargo(&'a Sysroot, &'a ManifestPath), -} - -pub fn get( - config: RustcDataLayoutConfig<'_>, - target: Option<&str>, - extra_env: &FxHashMap, -) -> anyhow::Result { - let process = |output: String| { - (|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))() - .ok_or_else(|| { - anyhow::format_err!("could not fetch target-spec-json from command output") - }) - }; - let sysroot = match config { - RustcDataLayoutConfig::Cargo(sysroot, cargo_toml) => { - let mut cmd = sysroot.tool(Tool::Cargo); - cmd.envs(extra_env); - cmd.current_dir(cargo_toml.parent()) - .args([ - "rustc", - "-Z", - "unstable-options", - "--print", - "target-spec-json", - "--", - "-Z", - "unstable-options", - ]) - .env("RUSTC_BOOTSTRAP", "1"); - if let Some(target) = target { - cmd.args(["--target", target]); - } - match utf8_stdout(cmd) { - Ok(output) => return process(output), - Err(e) => { - tracing::warn!("failed to run `cargo rustc --print target-spec-json`, falling back to invoking rustc directly: {e}"); - sysroot - } - } - } - RustcDataLayoutConfig::Rustc(sysroot) => sysroot, - }; - - let mut cmd = Sysroot::tool(sysroot, Tool::Rustc); - cmd.envs(extra_env) - .args(["-Z", "unstable-options", "--print", "target-spec-json"]) - .env("RUSTC_BOOTSTRAP", "1"); - if let Some(target) = target { - cmd.args(["--target", target]); - } - process(utf8_stdout(cmd)?) -} diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index f3cf2d83eaca..681bce3a5a64 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -12,39 +12,44 @@ use span::FileId; use triomphe::Arc; use crate::{ - sysroot::SysrootMode, workspace::ProjectWorkspaceKind, CargoWorkspace, CfgOverrides, - ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot, SysrootQueryMetadata, - WorkspaceBuildScripts, + sysroot::SysrootWorkspace, workspace::ProjectWorkspaceKind, CargoWorkspace, CfgOverrides, + ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot, + SysrootSourceWorkspaceConfig, WorkspaceBuildScripts, }; fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) { - load_cargo_with_overrides(file, CfgOverrides::default()) + let project_workspace = load_workspace_from_metadata(file); + to_crate_graph(project_workspace, &mut Default::default()) } fn load_cargo_with_overrides( file: &str, cfg_overrides: CfgOverrides, ) -> (CrateGraph, ProcMacroPaths) { + let project_workspace = + ProjectWorkspace { cfg_overrides, ..load_workspace_from_metadata(file) }; + to_crate_graph(project_workspace, &mut Default::default()) +} + +fn load_workspace_from_metadata(file: &str) -> ProjectWorkspace { let meta: Metadata = get_test_json_file(file); let manifest_path = ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); - let cargo_workspace = CargoWorkspace::new(meta, manifest_path); - let project_workspace = ProjectWorkspace { + let cargo_workspace = CargoWorkspace::new(meta, manifest_path, Default::default()); + ProjectWorkspace { kind: ProjectWorkspaceKind::Cargo { cargo: cargo_workspace, build_scripts: WorkspaceBuildScripts::default(), rustc: Err(None), - cargo_config_extra_env: Default::default(), error: None, set_test: true, }, - cfg_overrides, + cfg_overrides: Default::default(), sysroot: Sysroot::empty(), rustc_cfg: Vec::new(), toolchain: None, target_layout: Err("target_data_layout not loaded".into()), - }; - to_crate_graph(project_workspace) + } } fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) { @@ -59,7 +64,7 @@ fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) { target_layout: Err(Arc::from("test has no data layout")), cfg_overrides: Default::default(), }; - to_crate_graph(project_workspace) + to_crate_graph(project_workspace, &mut Default::default()) } fn get_test_json_file(file: &str) -> T { @@ -117,7 +122,9 @@ fn get_fake_sysroot() -> Sysroot { // fake sysroot, so we give them both the same path: let sysroot_dir = AbsPathBuf::assert(sysroot_path); let sysroot_src_dir = sysroot_dir.clone(); - Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir), SysrootQueryMetadata::CargoMetadata) + let mut sysroot = Sysroot::new(Some(sysroot_dir), Some(sysroot_src_dir)); + sysroot.load_workspace(&SysrootSourceWorkspaceConfig::default_cargo()); + sysroot } fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { @@ -128,13 +135,15 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { ProjectJson::new(None, base, data) } -fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacroPaths) { +fn to_crate_graph( + project_workspace: ProjectWorkspace, + file_map: &mut FxHashMap, +) -> (CrateGraph, ProcMacroPaths) { project_workspace.to_crate_graph( &mut { - let mut counter = 0; - move |_path| { - counter += 1; - Some(FileId::from_raw(counter)) + |path| { + let len = file_map.len() + 1; + Some(*file_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32))) } }, &Default::default(), @@ -222,25 +231,51 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() { crate_data.dependencies.iter().find(|&dep| dep.name.deref() == "proc_macro").unwrap(); } +#[test] +fn crate_graph_dedup_identical() { + let (mut crate_graph, proc_macros) = load_cargo("regex-metadata.json"); + + let (d_crate_graph, mut d_proc_macros) = (crate_graph.clone(), proc_macros.clone()); + + crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros); + assert!(crate_graph.iter().eq(d_crate_graph.iter())); + assert_eq!(proc_macros, d_proc_macros); +} + +#[test] +fn crate_graph_dedup() { + let mut file_map = Default::default(); + + let ripgrep_workspace = load_workspace_from_metadata("ripgrep-metadata.json"); + let (mut crate_graph, _proc_macros) = to_crate_graph(ripgrep_workspace, &mut file_map); + assert_eq!(crate_graph.iter().count(), 71); + + let regex_workspace = load_workspace_from_metadata("regex-metadata.json"); + let (regex_crate_graph, mut regex_proc_macros) = to_crate_graph(regex_workspace, &mut file_map); + assert_eq!(regex_crate_graph.iter().count(), 50); + + crate_graph.extend(regex_crate_graph, &mut regex_proc_macros); + assert_eq!(crate_graph.iter().count(), 108); +} + #[test] fn smoke_test_real_sysroot_cargo() { let file_map = &mut FxHashMap::::default(); let meta: Metadata = get_test_json_file("hello-world-metadata.json"); let manifest_path = ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); - let cargo_workspace = CargoWorkspace::new(meta, manifest_path); - let sysroot = Sysroot::discover( + let cargo_workspace = CargoWorkspace::new(meta, manifest_path, Default::default()); + let mut sysroot = Sysroot::discover( AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))), &Default::default(), - SysrootQueryMetadata::CargoMetadata, ); - assert!(matches!(sysroot.mode(), SysrootMode::Workspace(_))); + sysroot.load_workspace(&SysrootSourceWorkspaceConfig::default_cargo()); + assert!(matches!(sysroot.workspace(), SysrootWorkspace::Workspace(_))); let project_workspace = ProjectWorkspace { kind: ProjectWorkspaceKind::Cargo { cargo: cargo_workspace, build_scripts: WorkspaceBuildScripts::default(), rustc: Err(None), - cargo_config_extra_env: Default::default(), error: None, set_test: true, }, diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs new file mode 100644 index 000000000000..afcc81207949 --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs @@ -0,0 +1,124 @@ +//! Get the built-in cfg flags for the to be compile platform. + +use anyhow::Context; +use cfg::CfgAtom; +use rustc_hash::FxHashMap; +use toolchain::Tool; + +use crate::{toolchain_info::QueryConfig, utf8_stdout}; + +/// Uses `rustc --print cfg` to fetch the builtin cfgs. +pub fn get( + config: QueryConfig<'_>, + target: Option<&str>, + extra_env: &FxHashMap, +) -> Vec { + let _p = tracing::info_span!("rustc_cfg::get").entered(); + + let rustc_cfgs = rustc_print_cfg(target, extra_env, config); + let rustc_cfgs = match rustc_cfgs { + Ok(cfgs) => cfgs, + Err(e) => { + tracing::error!(?e, "failed to get rustc cfgs"); + return vec![]; + } + }; + + // These are unstable but the standard libraries gate on them. + let unstable = vec![ + r#"target_has_atomic_equal_alignment="8""#, + r#"target_has_atomic_equal_alignment="16""#, + r#"target_has_atomic_equal_alignment="32""#, + r#"target_has_atomic_equal_alignment="64""#, + r#"target_has_atomic_equal_alignment="128""#, + r#"target_has_atomic_equal_alignment="ptr""#, + r#"target_has_atomic_load_store"#, + r#"target_has_atomic_load_store="8""#, + r#"target_has_atomic_load_store="16""#, + r#"target_has_atomic_load_store="32""#, + r#"target_has_atomic_load_store="64""#, + r#"target_has_atomic_load_store="128""#, + r#"target_has_atomic_load_store="ptr""#, + r#"target_thread_local"#, + r#"target_has_atomic"#, + ]; + let rustc_cfgs = + rustc_cfgs.lines().chain(unstable).map(crate::parse_cfg).collect::, _>>(); + match rustc_cfgs { + Ok(rustc_cfgs) => { + tracing::debug!(?rustc_cfgs, "rustc cfgs found"); + rustc_cfgs + } + Err(e) => { + tracing::error!(?e, "failed to parse rustc cfgs"); + vec![] + } + } +} + +fn rustc_print_cfg( + target: Option<&str>, + extra_env: &FxHashMap, + config: QueryConfig<'_>, +) -> anyhow::Result { + const RUSTC_ARGS: [&str; 2] = ["--print", "cfg"]; + let (sysroot, current_dir) = match config { + QueryConfig::Cargo(sysroot, cargo_toml) => { + let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent()); + cmd.envs(extra_env); + cmd.args(["rustc"]).args(RUSTC_ARGS); + if let Some(target) = target { + cmd.args(["--target", target]); + } + cmd.args(["--", "-O"]); + + match utf8_stdout(&mut cmd) { + Ok(it) => return Ok(it), + Err(e) => { + tracing::warn!( + %e, + "failed to run `{cmd:?}`, falling back to invoking rustc directly" + ); + (sysroot, cargo_toml.parent().as_ref()) + } + } + } + QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir), + }; + + let mut cmd = sysroot.tool(Tool::Rustc, current_dir); + cmd.envs(extra_env); + cmd.args(RUSTC_ARGS); + cmd.arg("-O"); + if let Some(target) = target { + cmd.args(["--target", target]); + } + + utf8_stdout(&mut cmd).with_context(|| format!("unable to fetch cfgs via `{cmd:?}`")) +} + +#[cfg(test)] +mod tests { + use paths::{AbsPathBuf, Utf8PathBuf}; + + use crate::{ManifestPath, Sysroot}; + + use super::*; + + #[test] + fn cargo() { + let manifest_path = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml"); + let sysroot = Sysroot::empty(); + let manifest_path = + ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap(); + let cfg = QueryConfig::Cargo(&sysroot, &manifest_path); + assert_ne!(get(cfg, None, &FxHashMap::default()), vec![]); + } + + #[test] + fn rustc() { + let sysroot = Sysroot::empty(); + let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref()); + assert_ne!(get(cfg, None, &FxHashMap::default()), vec![]); + } +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs new file mode 100644 index 000000000000..94645a91f65b --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs @@ -0,0 +1,83 @@ +//! Runs `rustc --print target-spec-json` to get the target_data_layout. + +use anyhow::Context; +use rustc_hash::FxHashMap; +use toolchain::Tool; + +use crate::{toolchain_info::QueryConfig, utf8_stdout, Sysroot}; + +/// Uses `rustc --print target-spec-json`. +pub fn get( + config: QueryConfig<'_>, + target: Option<&str>, + extra_env: &FxHashMap, +) -> anyhow::Result { + const RUSTC_ARGS: [&str; 2] = ["--print", "target-spec-json"]; + let process = |output: String| { + (|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))() + .ok_or_else(|| { + anyhow::format_err!("could not parse target-spec-json from command output") + }) + }; + let (sysroot, current_dir) = match config { + QueryConfig::Cargo(sysroot, cargo_toml) => { + let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent()); + cmd.envs(extra_env); + cmd.env("RUSTC_BOOTSTRAP", "1"); + cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([ + "--", + "-Z", + "unstable-options", + ]); + if let Some(target) = target { + cmd.args(["--target", target]); + } + match utf8_stdout(&mut cmd) { + Ok(output) => return process(output), + Err(e) => { + tracing::warn!(%e, "failed to run `{cmd:?}`, falling back to invoking rustc directly"); + (sysroot, cargo_toml.parent().as_ref()) + } + } + } + QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir), + }; + + let mut cmd = Sysroot::tool(sysroot, Tool::Rustc, current_dir); + cmd.envs(extra_env) + .env("RUSTC_BOOTSTRAP", "1") + .args(["-Z", "unstable-options"]) + .args(RUSTC_ARGS); + if let Some(target) = target { + cmd.args(["--target", target]); + } + utf8_stdout(&mut cmd) + .with_context(|| format!("unable to fetch target-data-layout via `{cmd:?}`")) + .and_then(process) +} + +#[cfg(test)] +mod tests { + use paths::{AbsPathBuf, Utf8PathBuf}; + + use crate::{ManifestPath, Sysroot}; + + use super::*; + + #[test] + fn cargo() { + let manifest_path = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml"); + let sysroot = Sysroot::empty(); + let manifest_path = + ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap(); + let cfg = QueryConfig::Cargo(&sysroot, &manifest_path); + assert!(get(cfg, None, &FxHashMap::default()).is_ok()); + } + + #[test] + fn rustc() { + let sysroot = Sysroot::empty(); + let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref()); + assert!(get(cfg, None, &FxHashMap::default()).is_ok()); + } +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs new file mode 100644 index 000000000000..0476de58f230 --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs @@ -0,0 +1,105 @@ +//! Functionality to discover the current build target(s). +use std::path::Path; + +use anyhow::Context; +use rustc_hash::FxHashMap; +use toolchain::Tool; + +use crate::{toolchain_info::QueryConfig, utf8_stdout, ManifestPath, Sysroot}; + +/// For cargo, runs `cargo -Zunstable-options config get build.target` to get the configured project target(s). +/// For rustc, runs `rustc --print -vV` to get the host target. +pub fn get( + config: QueryConfig<'_>, + target: Option<&str>, + extra_env: &FxHashMap, +) -> anyhow::Result> { + let _p = tracing::info_span!("target_tuple::get").entered(); + if let Some(target) = target { + return Ok(vec![target.to_owned()]); + } + + let (sysroot, current_dir) = match config { + QueryConfig::Cargo(sysroot, cargo_toml) => { + match cargo_config_build_target(cargo_toml, extra_env, sysroot) { + Some(it) => return Ok(it), + None => (sysroot, cargo_toml.parent().as_ref()), + } + } + QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir), + }; + rustc_discover_host_tuple(extra_env, sysroot, current_dir).map(|it| vec![it]) +} + +fn rustc_discover_host_tuple( + extra_env: &FxHashMap, + sysroot: &Sysroot, + current_dir: &Path, +) -> anyhow::Result { + let mut cmd = sysroot.tool(Tool::Rustc, current_dir); + cmd.envs(extra_env); + cmd.arg("-vV"); + let stdout = utf8_stdout(&mut cmd) + .with_context(|| format!("unable to discover host platform via `{cmd:?}`"))?; + let field = "host: "; + let target = stdout.lines().find_map(|l| l.strip_prefix(field)); + if let Some(target) = target { + Ok(target.to_owned()) + } else { + // If we fail to resolve the host platform, it's not the end of the world. + Err(anyhow::format_err!("rustc -vV did not report host platform, got:\n{}", stdout)) + } +} + +fn cargo_config_build_target( + cargo_toml: &ManifestPath, + extra_env: &FxHashMap, + sysroot: &Sysroot, +) -> Option> { + let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent()); + cmd.envs(extra_env); + cmd.current_dir(cargo_toml.parent()).env("RUSTC_BOOTSTRAP", "1"); + cmd.args(["-Z", "unstable-options", "config", "get", "build.target"]); + // if successful we receive `build.target = "target-tuple"` + // or `build.target = ["", ..]` + // this might be `error: config value `build.target` is not set` in which case we + // don't wanna log the error + utf8_stdout(&mut cmd).and_then(parse_output_cargo_config_build_target).ok() +} + +// Parses `"build.target = [target-tuple, target-tuple, ...]"` or `"build.target = "target-tuple"` +fn parse_output_cargo_config_build_target(stdout: String) -> anyhow::Result> { + let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"'); + + if !trimmed.starts_with('[') { + return Ok([trimmed.to_owned()].to_vec()); + } + + serde_json::from_str(trimmed).context("Failed to parse `build.target` as an array of target") +} + +#[cfg(test)] +mod tests { + use paths::{AbsPathBuf, Utf8PathBuf}; + + use crate::{ManifestPath, Sysroot}; + + use super::*; + + #[test] + fn cargo() { + let manifest_path = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml"); + let sysroot = Sysroot::empty(); + let manifest_path = + ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap(); + let cfg = QueryConfig::Cargo(&sysroot, &manifest_path); + assert!(get(cfg, None, &FxHashMap::default()).is_ok()); + } + + #[test] + fn rustc() { + let sysroot = Sysroot::empty(); + let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref()); + assert!(get(cfg, None, &FxHashMap::default()).is_ok()); + } +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs new file mode 100644 index 000000000000..e795fdf1d64f --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs @@ -0,0 +1,58 @@ +//! Get the version string of the toolchain. + +use anyhow::Context; +use rustc_hash::FxHashMap; +use semver::Version; +use toolchain::Tool; + +use crate::{toolchain_info::QueryConfig, utf8_stdout}; + +pub(crate) fn get( + config: QueryConfig<'_>, + extra_env: &FxHashMap, +) -> Result, anyhow::Error> { + let (mut cmd, prefix) = match config { + QueryConfig::Cargo(sysroot, cargo_toml) => { + (sysroot.tool(Tool::Cargo, cargo_toml.parent()), "cargo ") + } + QueryConfig::Rustc(sysroot, current_dir) => { + (sysroot.tool(Tool::Rustc, current_dir), "rustc ") + } + }; + cmd.envs(extra_env); + cmd.arg("--version"); + let out = utf8_stdout(&mut cmd).with_context(|| format!("Failed to query rust toolchain version via `{cmd:?}`, is your toolchain setup correctly?"))?; + + let version = + out.strip_prefix(prefix).and_then(|it| Version::parse(it.split_whitespace().next()?).ok()); + if version.is_none() { + tracing::warn!("Failed to parse `{cmd:?}` output `{out}` as a semver version"); + } + anyhow::Ok(version) +} + +#[cfg(test)] +mod tests { + use paths::{AbsPathBuf, Utf8PathBuf}; + + use crate::{ManifestPath, Sysroot}; + + use super::*; + + #[test] + fn cargo() { + let manifest_path = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml"); + let sysroot = Sysroot::empty(); + let manifest_path = + ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap(); + let cfg = QueryConfig::Cargo(&sysroot, &manifest_path); + assert!(get(cfg, &FxHashMap::default()).is_ok()); + } + + #[test] + fn rustc() { + let sysroot = Sysroot::empty(); + let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref()); + assert!(get(cfg, &FxHashMap::default()).is_ok()); + } +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 71ddee309100..a345c6bcce45 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -2,7 +2,7 @@ //! metadata` or `rust-project.json`) into representation stored in the salsa //! database -- `CrateGraph`. -use std::{collections::VecDeque, fmt, fs, iter, sync}; +use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync}; use anyhow::Context; use base_db::{ @@ -16,20 +16,19 @@ use paths::{AbsPath, AbsPathBuf}; use rustc_hash::FxHashMap; use semver::Version; use span::{Edition, FileId}; -use toolchain::Tool; use tracing::instrument; use triomphe::Arc; use crate::{ build_dependencies::BuildScriptOutput, - cargo_workspace::{DepKind, PackageData, RustLibSource}, + cargo_workspace::{CargoMetadataConfig, DepKind, PackageData, RustLibSource}, env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env}, project_json::{Crate, CrateArrayIdx}, - rustc_cfg::{self, RustcCfgConfig}, - sysroot::{SysrootCrate, SysrootMode}, - target_data_layout::{self, RustcDataLayoutConfig}, - utf8_stdout, CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, - Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts, + sysroot::{SysrootCrate, SysrootWorkspace}, + toolchain_info::{rustc_cfg, target_data_layout, target_tuple, version, QueryConfig}, + CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, Package, + ProjectJson, ProjectManifest, Sysroot, SysrootSourceWorkspaceConfig, TargetData, TargetKind, + WorkspaceBuildScripts, }; use tracing::{debug, error, info}; @@ -79,8 +78,6 @@ pub enum ProjectWorkspaceKind { /// The rustc workspace loaded for this workspace. An `Err(None)` means loading has been /// disabled or was otherwise not requested. rustc: Result, Option>, - /// Environment variables set in the `.cargo/config` file. - cargo_config_extra_env: FxHashMap, set_test: bool, }, /// Project workspace was specified using a `rust-project.json` file. @@ -100,8 +97,6 @@ pub enum ProjectWorkspaceKind { file: ManifestPath, /// Is this file a cargo script file? cargo: Option<(CargoWorkspace, WorkspaceBuildScripts, Option>)>, - /// Environment variables set in the `.cargo/config` file. - cargo_config_extra_env: FxHashMap, set_test: bool, }, } @@ -111,14 +106,7 @@ impl fmt::Debug for ProjectWorkspace { // Make sure this isn't too verbose. let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides } = self; match kind { - ProjectWorkspaceKind::Cargo { - cargo, - error: _, - build_scripts, - rustc, - cargo_config_extra_env, - set_test, - } => f + ProjectWorkspaceKind::Cargo { cargo, error: _, build_scripts, rustc, set_test } => f .debug_struct("Cargo") .field("root", &cargo.workspace_root().file_name()) .field("n_packages", &cargo.packages().len()) @@ -131,7 +119,6 @@ impl fmt::Debug for ProjectWorkspace { .field("n_cfg_overrides", &cfg_overrides.len()) .field("toolchain", &toolchain) .field("data_layout", &target_layout) - .field("cargo_config_extra_env", &cargo_config_extra_env) .field("set_test", set_test) .field("build_scripts", &build_scripts.error().unwrap_or("ok")) .finish(), @@ -147,12 +134,7 @@ impl fmt::Debug for ProjectWorkspace { debug_struct.finish() } - ProjectWorkspaceKind::DetachedFile { - file, - cargo: cargo_script, - cargo_config_extra_env, - set_test, - } => f + ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, set_test } => f .debug_struct("DetachedFiles") .field("file", &file) .field("cargo_script", &cargo_script.is_some()) @@ -162,34 +144,12 @@ impl fmt::Debug for ProjectWorkspace { .field("toolchain", &toolchain) .field("data_layout", &target_layout) .field("n_cfg_overrides", &cfg_overrides.len()) - .field("cargo_config_extra_env", &cargo_config_extra_env) .field("set_test", set_test) .finish(), } } } -fn get_toolchain_version( - current_dir: &AbsPath, - sysroot: &Sysroot, - tool: Tool, - extra_env: &FxHashMap, - prefix: &str, -) -> Result, anyhow::Error> { - let cargo_version = utf8_stdout({ - let mut cmd = Sysroot::tool(sysroot, tool); - cmd.envs(extra_env); - cmd.arg("--version").current_dir(current_dir); - cmd - }) - .with_context(|| format!("Failed to query rust toolchain version at {current_dir}, is your toolchain setup correctly?"))?; - anyhow::Ok( - cargo_version - .get(prefix.len()..) - .and_then(|it| Version::parse(it.split_whitespace().next()?).ok()), - ) -} - impl ProjectWorkspace { pub fn load( manifest: ProjectManifest, @@ -220,167 +180,159 @@ impl ProjectWorkspace { ProjectWorkspace::load_detached_file(rust_file, config)? } ProjectManifest::CargoToml(cargo_toml) => { - let sysroot = match (&config.sysroot, &config.sysroot_src) { - (Some(RustLibSource::Discover), None) => Sysroot::discover( - cargo_toml.parent(), - &config.extra_env, - config.sysroot_query_metadata, - ), - (Some(RustLibSource::Discover), Some(sysroot_src)) => { - Sysroot::discover_with_src_override( - cargo_toml.parent(), - &config.extra_env, - sysroot_src.clone(), - config.sysroot_query_metadata, - ) - } - (Some(RustLibSource::Path(path)), None) => Sysroot::discover_sysroot_src_dir( - path.clone(), - config.sysroot_query_metadata, - ), - (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => Sysroot::load( - Some(sysroot.clone()), - Some(sysroot_src.clone()), - config.sysroot_query_metadata, - ), - (None, _) => Sysroot::empty(), - }; - tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot"); - - let rustc_dir = match &config.rustc_source { - Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone()) - .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))), - Some(RustLibSource::Discover) => { - sysroot.discover_rustc_src().ok_or_else(|| { - Some("Failed to discover rustc source for sysroot.".to_owned()) - }) - } - None => Err(None), - }; - - let rustc = rustc_dir.and_then(|rustc_dir| { - info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source"); - match CargoWorkspace::fetch_metadata( - &rustc_dir, - cargo_toml.parent(), - &CargoConfig { - features: crate::CargoFeatures::default(), - ..config.clone() - }, - &sysroot, - false, - progress, - ) { - Ok((meta, _error)) => { - let workspace = CargoWorkspace::new(meta, cargo_toml.clone()); - let buildscripts = WorkspaceBuildScripts::rustc_crates( - &workspace, - cargo_toml.parent(), - &config.extra_env, - &sysroot - ); - Ok(Box::new((workspace, buildscripts))) - } - Err(e) => { - tracing::error!( - %e, - "Failed to read Cargo metadata from rustc source at {rustc_dir}", - ); - Err(Some(format!( - "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}" - ))) - } - } - }); - - let toolchain = get_toolchain_version( - cargo_toml.parent(), - &sysroot, - Tool::Cargo, - &config.extra_env, - "cargo ", - )?; - let rustc_cfg = rustc_cfg::get( - config.target.as_deref(), - &config.extra_env, - RustcCfgConfig::Cargo(&sysroot, cargo_toml), - ); - - let cfg_overrides = config.cfg_overrides.clone(); - let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Cargo(&sysroot, cargo_toml), - config.target.as_deref(), - &config.extra_env, - ); - if let Err(e) = &data_layout { - tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace"); - } - - let (meta, error) = CargoWorkspace::fetch_metadata( - cargo_toml, - cargo_toml.parent(), - config, - &sysroot, - false, - progress, - ) - .with_context(|| { - format!( - "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}", - ) - })?; - let cargo = CargoWorkspace::new(meta, cargo_toml.clone()); - - let cargo_config_extra_env = - cargo_config_env(cargo_toml, &config.extra_env, &sysroot); - ProjectWorkspace { - kind: ProjectWorkspaceKind::Cargo { - cargo, - build_scripts: WorkspaceBuildScripts::default(), - rustc, - cargo_config_extra_env, - error: error.map(Arc::new), - set_test: config.set_test, - }, - sysroot, - rustc_cfg, - cfg_overrides, - toolchain, - target_layout: data_layout - .map(Arc::from) - .map_err(|it| Arc::from(it.to_string())), - } + ProjectWorkspace::load_cargo(cargo_toml, config, progress)? } }; Ok(res) } - pub fn load_inline(project_json: ProjectJson, config: &CargoConfig) -> ProjectWorkspace { - let sysroot = Sysroot::load( - project_json.sysroot.clone(), - project_json.sysroot_src.clone(), - config.sysroot_query_metadata, - ); - let cfg_config = RustcCfgConfig::Rustc(&sysroot); - let data_layout_config = RustcDataLayoutConfig::Rustc(&sysroot); - let toolchain = match get_toolchain_version( - project_json.path(), - &sysroot, - Tool::Rustc, - &config.extra_env, - "rustc ", - ) { - Ok(it) => it, - Err(e) => { - tracing::error!("{e}"); - None + fn load_cargo( + cargo_toml: &ManifestPath, + config: &CargoConfig, + progress: &dyn Fn(String), + ) -> Result { + let mut sysroot = match (&config.sysroot, &config.sysroot_src) { + (Some(RustLibSource::Discover), None) => { + Sysroot::discover(cargo_toml.parent(), &config.extra_env) } + (Some(RustLibSource::Discover), Some(sysroot_src)) => { + Sysroot::discover_with_src_override( + cargo_toml.parent(), + &config.extra_env, + sysroot_src.clone(), + ) + } + (Some(RustLibSource::Path(path)), None) => { + Sysroot::discover_sysroot_src_dir(path.clone()) + } + (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => { + Sysroot::new(Some(sysroot.clone()), Some(sysroot_src.clone())) + } + (None, _) => Sysroot::empty(), }; + let rustc_dir = match &config.rustc_source { + Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone()) + .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))), + Some(RustLibSource::Discover) => sysroot + .discover_rustc_src() + .ok_or_else(|| Some("Failed to discover rustc source for sysroot.".to_owned())), + None => Err(None), + }; + + tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot"); + let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml); + let targets = + target_tuple::get(toolchain_config, config.target.as_deref(), &config.extra_env) + .unwrap_or_default(); + let toolchain = version::get(toolchain_config, &config.extra_env) + .inspect_err(|e| { + tracing::error!(%e, + "failed fetching toolchain version for {cargo_toml:?} workspace" + ) + }) + .ok() + .flatten(); + let rustc_cfg = + rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), &config.extra_env); + let cfg_overrides = config.cfg_overrides.clone(); + let data_layout = target_data_layout::get( + toolchain_config, + targets.first().map(Deref::deref), + &config.extra_env, + ); + if let Err(e) = &data_layout { + tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace"); + } + sysroot.load_workspace(&SysrootSourceWorkspaceConfig::CargoMetadata( + sysroot_metadata_config(&config.extra_env, &targets), + )); + + let rustc = rustc_dir.and_then(|rustc_dir| { + info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source"); + match CargoWorkspace::fetch_metadata( + &rustc_dir, + cargo_toml.parent(), + &CargoMetadataConfig { + features: crate::CargoFeatures::default(), + targets: targets.clone(), + extra_args: config.extra_args.clone(), + extra_env: config.extra_env.clone(), + }, + &sysroot, + false, + progress, + ) { + Ok((meta, _error)) => { + let workspace = CargoWorkspace::new(meta, cargo_toml.clone(), Env::default()); + let build_scripts = WorkspaceBuildScripts::rustc_crates( + &workspace, + cargo_toml.parent(), + &config.extra_env, + &sysroot, + ); + Ok(Box::new((workspace, build_scripts))) + } + Err(e) => { + tracing::error!( + %e, + "Failed to read Cargo metadata from rustc source at {rustc_dir}", + ); + Err(Some(format!( + "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}" + ))) + } + } + }); + + let (meta, error) = CargoWorkspace::fetch_metadata( + cargo_toml, + cargo_toml.parent(), + &CargoMetadataConfig { + features: config.features.clone(), + targets, + extra_args: config.extra_args.clone(), + extra_env: config.extra_env.clone(), + }, + &sysroot, + false, + progress, + ) + .with_context(|| { + format!( + "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}", + ) + })?; + let cargo_config_extra_env = cargo_config_env(cargo_toml, &config.extra_env, &sysroot); + let cargo = CargoWorkspace::new(meta, cargo_toml.clone(), cargo_config_extra_env); + + Ok(ProjectWorkspace { + kind: ProjectWorkspaceKind::Cargo { + cargo, + build_scripts: WorkspaceBuildScripts::default(), + rustc, + error: error.map(Arc::new), + set_test: config.set_test, + }, + sysroot, + rustc_cfg, + cfg_overrides, + toolchain, + target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + }) + } + + pub fn load_inline(project_json: ProjectJson, config: &CargoConfig) -> ProjectWorkspace { + let mut sysroot = + Sysroot::new(project_json.sysroot.clone(), project_json.sysroot_src.clone()); + sysroot.load_workspace(&SysrootSourceWorkspaceConfig::Stitched); + let query_config = QueryConfig::Rustc(&sysroot, project_json.path().as_ref()); + let toolchain = version::get(query_config, &config.extra_env).ok().flatten(); + let target = config.target.as_deref(); - let rustc_cfg = rustc_cfg::get(target, &config.extra_env, cfg_config); - let data_layout = target_data_layout::get(data_layout_config, target, &config.extra_env); + let rustc_cfg = rustc_cfg::get(query_config, target, &config.extra_env); + let data_layout = target_data_layout::get(query_config, target, &config.extra_env); ProjectWorkspace { kind: ProjectWorkspaceKind::Json(project_json), sysroot, @@ -396,49 +348,50 @@ impl ProjectWorkspace { config: &CargoConfig, ) -> anyhow::Result { let dir = detached_file.parent(); - let sysroot = match &config.sysroot { - Some(RustLibSource::Path(path)) => { - Sysroot::discover_sysroot_src_dir(path.clone(), config.sysroot_query_metadata) - } - Some(RustLibSource::Discover) => { - Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata) - } + let mut sysroot = match &config.sysroot { + Some(RustLibSource::Path(path)) => Sysroot::discover_sysroot_src_dir(path.clone()), + Some(RustLibSource::Discover) => Sysroot::discover(dir, &config.extra_env), None => Sysroot::empty(), }; - let toolchain = - match get_toolchain_version(dir, &sysroot, Tool::Rustc, &config.extra_env, "rustc ") { - Ok(it) => it, - Err(e) => { - tracing::error!("{e}"); - None - } - }; + let query_config = QueryConfig::Cargo(&sysroot, detached_file); + let toolchain = version::get(query_config, &config.extra_env).ok().flatten(); + let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env) + .unwrap_or_default(); + let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env); + let data_layout = target_data_layout::get(query_config, None, &config.extra_env); + sysroot.load_workspace(&SysrootSourceWorkspaceConfig::CargoMetadata( + sysroot_metadata_config(&config.extra_env, &targets), + )); - let rustc_cfg = rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(&sysroot)); - let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Rustc(&sysroot), - None, - &config.extra_env, - ); + let cargo_script = CargoWorkspace::fetch_metadata( + detached_file, + dir, + &CargoMetadataConfig { + features: config.features.clone(), + targets, + extra_args: config.extra_args.clone(), + extra_env: config.extra_env.clone(), + }, + &sysroot, + false, + &|_| (), + ) + .ok() + .map(|(ws, error)| { + let cargo_config_extra_env = + cargo_config_env(detached_file, &config.extra_env, &sysroot); + ( + CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env), + WorkspaceBuildScripts::default(), + error.map(Arc::new), + ) + }); - let cargo_script = - CargoWorkspace::fetch_metadata(detached_file, dir, config, &sysroot, false, &|_| ()) - .ok() - .map(|(ws, error)| { - ( - CargoWorkspace::new(ws, detached_file.clone()), - WorkspaceBuildScripts::default(), - error.map(Arc::new), - ) - }); - - let cargo_config_extra_env = cargo_config_env(detached_file, &config.extra_env, &sysroot); Ok(ProjectWorkspace { kind: ProjectWorkspaceKind::DetachedFile { file: detached_file.to_owned(), cargo: cargo_script, - cargo_config_extra_env, set_test: config.set_test, }, sysroot, @@ -565,8 +518,8 @@ impl ProjectWorkspace { /// the root is a member of the current workspace pub fn to_roots(&self) -> Vec { let mk_sysroot = || { - let mut r = match self.sysroot.mode() { - SysrootMode::Workspace(ws) => ws + let mut r = match self.sysroot.workspace() { + SysrootWorkspace::Workspace(ws) => ws .packages() .filter_map(|pkg| { if ws[pkg].is_local { @@ -587,7 +540,7 @@ impl ProjectWorkspace { Some(PackageRoot { is_local: false, include, exclude }) }) .collect(), - SysrootMode::Stitched(_) | SysrootMode::Empty => vec![], + SysrootWorkspace::Stitched(_) | SysrootWorkspace::Empty => vec![], }; r.push(PackageRoot { @@ -601,32 +554,25 @@ impl ProjectWorkspace { ProjectWorkspaceKind::Json(project) => project .crates() .map(|(_, krate)| { - let build_files = project - .crates() - .filter_map(|(_, krate)| { - krate.build.as_ref().map(|build| build.build_file.clone()) - }) - // FIXME: PackageRoots dont allow specifying files, only directories - .filter_map(|build_file| { - self.workspace_root().join(build_file).parent().map(ToOwned::to_owned) - }); + // FIXME: PackageRoots dont allow specifying files, only directories + let build_file = krate + .build + .as_ref() + .map(|build| self.workspace_root().join(&build.build_file)) + .as_deref() + .and_then(AbsPath::parent) + .map(ToOwned::to_owned); + PackageRoot { is_local: krate.is_workspace_member, - include: krate.include.iter().cloned().chain(build_files).collect(), + include: krate.include.iter().cloned().chain(build_file).collect(), exclude: krate.exclude.clone(), } }) .chain(mk_sysroot()) .unique() .collect(), - ProjectWorkspaceKind::Cargo { - cargo, - rustc, - build_scripts, - cargo_config_extra_env: _, - error: _, - set_test: _, - } => { + ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _, set_test: _ } => { cargo .packages() .map(|pkg| { @@ -763,23 +709,18 @@ impl ProjectWorkspace { extra_env, cfg_overrides, ), - ProjectWorkspaceKind::Cargo { - cargo, - rustc, - build_scripts, - cargo_config_extra_env: _, - error: _, - set_test, - } => cargo_to_crate_graph( - load, - rustc.as_ref().map(|a| a.as_ref()).ok(), - cargo, - sysroot, - rustc_cfg.clone(), - cfg_overrides, - build_scripts, - *set_test, - ), + ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _, set_test } => { + cargo_to_crate_graph( + load, + rustc.as_ref().map(|a| a.as_ref()).ok(), + cargo, + sysroot, + rustc_cfg.clone(), + cfg_overrides, + build_scripts, + *set_test, + ) + } ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, set_test, .. } => { if let Some((cargo, build_scripts, _)) = cargo_script { cargo_to_crate_graph( @@ -824,7 +765,6 @@ impl ProjectWorkspace { ProjectWorkspaceKind::Cargo { cargo, rustc, - cargo_config_extra_env, build_scripts: _, error: _, set_test: _, @@ -832,16 +772,11 @@ impl ProjectWorkspace { ProjectWorkspaceKind::Cargo { cargo: o_cargo, rustc: o_rustc, - cargo_config_extra_env: o_cargo_config_extra_env, build_scripts: _, error: _, set_test: _, }, - ) => { - cargo == o_cargo - && rustc == o_rustc - && cargo_config_extra_env == o_cargo_config_extra_env - } + ) => cargo == o_cargo && rustc == o_rustc, (ProjectWorkspaceKind::Json(project), ProjectWorkspaceKind::Json(o_project)) => { project == o_project } @@ -849,20 +784,14 @@ impl ProjectWorkspace { ProjectWorkspaceKind::DetachedFile { file, cargo: Some((cargo_script, _, _)), - cargo_config_extra_env, set_test: _, }, ProjectWorkspaceKind::DetachedFile { file: o_file, cargo: Some((o_cargo_script, _, _)), - cargo_config_extra_env: o_cargo_config_extra_env, set_test: _, }, - ) => { - file == o_file - && cargo_script == o_cargo_script - && cargo_config_extra_env == o_cargo_config_extra_env - } + ) => file == o_file && cargo_script == o_cargo_script, _ => return false, }) && sysroot == o_sysroot && rustc_cfg == o_rustc_cfg @@ -921,7 +850,11 @@ fn project_json_to_crate_graph( let target_cfgs = match target.as_deref() { Some(target) => cfg_cache.entry(target).or_insert_with(|| { - rustc_cfg::get(Some(target), extra_env, RustcCfgConfig::Rustc(sysroot)) + rustc_cfg::get( + QueryConfig::Rustc(sysroot, project.project_root().as_ref()), + Some(target), + extra_env, + ) }), None => &rustc_cfg, }; @@ -1374,15 +1307,13 @@ fn add_target_crate_root( opts }; - let mut env = Env::default(); + let mut env = cargo.env().clone(); inject_cargo_package_env(&mut env, pkg); inject_cargo_env(&mut env); - inject_rustc_tool_env(&mut env, cargo, cargo_name, kind); + inject_rustc_tool_env(&mut env, cargo_name, kind); if let Some(envs) = build_data.map(|(it, _)| &it.envs) { - for (k, v) in envs { - env.set(k, v.clone()); - } + env.extend_from_other(envs); } let crate_id = crate_graph.add_crate_root( file_id, @@ -1433,8 +1364,8 @@ fn sysroot_to_crate_graph( load: FileLoader<'_>, ) -> (SysrootPublicDeps, Option) { let _p = tracing::info_span!("sysroot_to_crate_graph").entered(); - match sysroot.mode() { - SysrootMode::Workspace(cargo) => { + match sysroot.workspace() { + SysrootWorkspace::Workspace(cargo) => { let (mut cg, mut pm) = cargo_to_crate_graph( load, None, @@ -1509,7 +1440,7 @@ fn sysroot_to_crate_graph( (SysrootPublicDeps { deps: pub_deps }, libproc_macro) } - SysrootMode::Stitched(stitched) => { + SysrootWorkspace::Stitched(stitched) => { let cfg_options = Arc::new({ let mut cfg_options = CfgOptions::default(); cfg_options.extend(rustc_cfg); @@ -1562,7 +1493,7 @@ fn sysroot_to_crate_graph( stitched.proc_macro().and_then(|it| sysroot_crates.get(&it).copied()); (public_deps, libproc_macro) } - SysrootMode::Empty => (SysrootPublicDeps { deps: vec![] }, None), + SysrootWorkspace::Empty => (SysrootPublicDeps { deps: vec![] }, None), } } @@ -1597,3 +1528,15 @@ fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) { tracing::warn!("{}", err) } } + +fn sysroot_metadata_config( + extra_env: &FxHashMap, + targets: &[String], +) -> CargoMetadataConfig { + CargoMetadataConfig { + features: Default::default(), + targets: targets.to_vec(), + extra_args: Default::default(), + extra_env: extra_env.clone(), + } +} diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt index 90f41a9c2fc8..2026ab2b8c2f 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt @@ -479,7 +479,7 @@ }, 11: CrateData { root_file_id: FileId( - 12, + 11, ), edition: Edition2018, version: None, diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json new file mode 100644 index 000000000000..371464dd20aa --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json @@ -0,0 +1,6420 @@ +{ + "packages": [ + { + "name": "aho-corasick", + "version": "0.7.20", + "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast multiple substring searching.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "aho_corasick", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "default": [ + "std" + ], + "std": [ + "memchr/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "string", + "search", + "text", + "aho", + "multi" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/aho-corasick", + "homepage": "https://github.com/BurntSushi/aho-corasick", + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cc", + "version": "1.0.79", + "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "jobserver", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.16", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempfile", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "gcc-shim", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cc_env", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cxxflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "jobserver": [ + "dep:jobserver" + ], + "parallel": [ + "jobserver" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [ + "development-tools::build-utils" + ], + "keywords": [ + "build-dependencies" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/cc-rs", + "homepage": "https://github.com/rust-lang/cc-rs", + "documentation": "https://docs.rs/cc", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cfg-if", + "version": "0.1.10", + "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cfg-if", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "xcrate", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/tests/xcrate.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/alexcrichton/cfg-if", + "homepage": "https://github.com/alexcrichton/cfg-if", + "documentation": "https://docs.rs/cfg-if", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cfg-if", + "version": "1.0.0", + "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cfg-if", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "xcrate", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/alexcrichton/cfg-if", + "homepage": "https://github.com/alexcrichton/cfg-if", + "documentation": "https://docs.rs/cfg-if", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "docopt", + "version": "1.1.1", + "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Command line argument parsing.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.4.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "std", + "unicode" + ], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "strsim", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.10", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "docopt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "docopt-wordlist", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/wordlist.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "cargo", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cargo.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "cp", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cp.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "decode", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/decode.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "hashmap", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/hashmap.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "optional_command", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/optional_command.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "verbose_multiple", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/verbose_multiple.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "command-line-interface" + ], + "keywords": [ + "docopt", + "argument", + "command", + "argv" + ], + "readme": "README.md", + "repository": "https://github.com/docopt/docopt.rs", + "homepage": "https://github.com/docopt/docopt.rs", + "documentation": "http://burntsushi.net/rustdoc/docopt/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "getrandom", + "version": "0.2.9", + "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A small cross-platform library for retrieving random data from system source", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "js-sys", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", + "registry": null + }, + { + "name": "wasm-bindgen", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.62", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", + "registry": null + }, + { + "name": "wasm-bindgen-test", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.18", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", + "registry": null + }, + { + "name": "wasi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": "cfg(target_os = \"wasi\")", + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.139", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": "cfg(unix)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "getrandom", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "custom", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/custom.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "normal", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/normal.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "rdrand", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/rdrand.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "buffer", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/benches/buffer.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "custom": [], + "js": [ + "wasm-bindgen", + "js-sys" + ], + "js-sys": [ + "dep:js-sys" + ], + "rdrand": [], + "rustc-dep-of-std": [ + "compiler_builtins", + "core", + "libc/rustc-dep-of-std", + "wasi/rustc-dep-of-std" + ], + "std": [], + "test-in-browser": [], + "wasm-bindgen": [ + "dep:wasm-bindgen" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "std", + "custom" + ], + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rand Project Developers" + ], + "categories": [ + "os", + "no-std" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-random/getrandom", + "homepage": null, + "documentation": "https://docs.rs/getrandom", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "lazy_static", + "version": "1.4.0", + "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro for declaring lazily evaluated statics in Rust.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "spin", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "lazy_static", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "no_std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "spin": [ + "dep:spin" + ], + "spin_no_std": [ + "spin" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Marvin Löbel " + ], + "categories": [ + "no-std", + "rust-patterns", + "memory-management" + ], + "keywords": [ + "macro", + "lazy", + "static" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang-nursery/lazy-static.rs", + "homepage": null, + "documentation": "https://docs.rs/lazy_static", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "libc", + "version": "0.2.142", + "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Raw FFI bindings to platform libraries like libc.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "libc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "const_fn", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "align": [], + "const-extern-fn": [], + "default": [ + "std" + ], + "extra_traits": [], + "rustc-dep-of-std": [ + "align", + "rustc-std-workspace-core" + ], + "rustc-std-workspace-core": [ + "dep:rustc-std-workspace-core" + ], + "std": [], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "const-extern-fn", + "extra_traits" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "external-ffi-bindings", + "no-std", + "os" + ], + "keywords": [ + "libc", + "ffi", + "bindings", + "operating", + "system" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/libc", + "homepage": "https://github.com/rust-lang/libc", + "documentation": "https://docs.rs/libc/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "memchr", + "version": "2.5.0", + "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Safe interface to memchr.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.18", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "memchr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [ + "std" + ], + "libc": [ + "dep:libc" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ], + "std": [], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant ", + "bluss" + ], + "categories": [], + "keywords": [ + "memchr", + "char", + "scan", + "strchr", + "string" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/memchr", + "homepage": "https://github.com/BurntSushi/memchr", + "documentation": "https://docs.rs/memchr/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "memmap", + "version": "0.6.2", + "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Cross-platform Rust API for memory-mapped file IO", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "tempdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(unix)", + "registry": null + }, + { + "name": "winapi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "basetsd", + "handleapi", + "memoryapi", + "minwindef", + "std", + "sysinfoapi" + ], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "memmap", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "cat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/examples/cat.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Dan Burkert " + ], + "categories": [], + "keywords": [ + "mmap", + "memory-map", + "io", + "file" + ], + "readme": "README.md", + "repository": "https://github.com/danburkert/memmap-rs", + "homepage": null, + "documentation": "https://docs.rs/memmap", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "pkg-config", + "version": "0.3.26", + "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "pkg-config", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [ + "build-dependencies" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/pkg-config-rs", + "homepage": null, + "documentation": "https://docs.rs/pkg-config", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "proc-macro2", + "version": "1.0.56", + "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "unicode-ident", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "proc-macro2", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "comments", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "features", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "marker", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_fmt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "proc-macro" + ], + "nightly": [], + "proc-macro": [], + "span-locations": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "rustc-args": [ + "--cfg", + "procmacro2_semver_exempt" + ], + "rustdoc-args": [ + "--cfg", + "procmacro2_semver_exempt", + "--cfg", + "doc_cfg" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "span-locations" + ] + } + }, + "publish": null, + "authors": [ + "David Tolnay ", + "Alex Crichton " + ], + "categories": [ + "development-tools::procedural-macro-helpers" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/proc-macro2", + "homepage": null, + "documentation": "https://docs.rs/proc-macro2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "quickcheck", + "version": "1.0.3", + "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Automatic property based testing with shrinking.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "env_logger", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "getrandom", + "small_rng" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "quickcheck", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "btree_set_range", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/btree_set_range.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "out_of_bounds", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/out_of_bounds.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "reverse", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "reverse_single", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse_single.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "sieve", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sieve.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "sort", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sort.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "regex", + "use_logging" + ], + "env_logger": [ + "dep:env_logger" + ], + "log": [ + "dep:log" + ], + "regex": [ + "env_logger/regex" + ], + "use_logging": [ + "log", + "env_logger" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "development-tools::testing" + ], + "keywords": [ + "testing", + "quickcheck", + "property", + "shrinking", + "fuzz" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/quickcheck", + "homepage": "https://github.com/BurntSushi/quickcheck", + "documentation": "https://docs.rs/quickcheck", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "quote", + "version": "1.0.26", + "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Quasi-quoting macro quote!(...)", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.52", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "trybuild", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.66", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "diff" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "quote", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compiletest", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "proc-macro" + ], + "proc-macro": [ + "proc-macro2/proc-macro" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "development-tools::procedural-macro-helpers" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/quote", + "homepage": null, + "documentation": "https://docs.rs/quote/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "rand", + "version": "0.8.5", + "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Random number generators and other randomness functionality.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.4", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "packed_simd_2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.7", + "kind": null, + "rename": "packed_simd", + "optional": true, + "uses_default_features": true, + "features": [ + "into_bits" + ], + "target": null, + "registry": null + }, + { + "name": "rand_chacha", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand_core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.103", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "bincode", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.2.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand_pcg", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.22", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": "cfg(unix)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "rand", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "alloc": [ + "rand_core/alloc" + ], + "default": [ + "std", + "std_rng" + ], + "getrandom": [ + "rand_core/getrandom" + ], + "libc": [ + "dep:libc" + ], + "log": [ + "dep:log" + ], + "min_const_gen": [], + "nightly": [], + "packed_simd": [ + "dep:packed_simd" + ], + "rand_chacha": [ + "dep:rand_chacha" + ], + "serde": [ + "dep:serde" + ], + "serde1": [ + "serde", + "rand_core/serde1" + ], + "simd_support": [ + "packed_simd" + ], + "small_rng": [], + "std": [ + "rand_core/std", + "rand_chacha/std", + "alloc", + "getrandom", + "libc" + ], + "std_rng": [ + "rand_chacha" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "doc_cfg" + ] + } + }, + "playground": { + "features": [ + "small_rng", + "serde1" + ] + } + }, + "publish": null, + "authors": [ + "The Rand Project Developers", + "The Rust Project Developers" + ], + "categories": [ + "algorithms", + "no-std" + ], + "keywords": [ + "random", + "rng" + ], + "readme": "README.md", + "repository": "https://github.com/rust-random/rand", + "homepage": "https://rust-random.github.io/book", + "documentation": "https://docs.rs/rand", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "rand_core", + "version": "0.6.4", + "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Core random number generator traits and tools for implementation.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "getrandom", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "rand_core", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "alloc": [], + "getrandom": [ + "dep:getrandom" + ], + "serde": [ + "dep:serde" + ], + "serde1": [ + "serde" + ], + "std": [ + "alloc", + "getrandom", + "getrandom/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "doc_cfg" + ] + } + }, + "playground": { + "all-features": true + } + }, + "publish": null, + "authors": [ + "The Rand Project Developers", + "The Rust Project Developers" + ], + "categories": [ + "algorithms", + "no-std" + ], + "keywords": [ + "random", + "rng" + ], + "readme": "README.md", + "repository": "https://github.com/rust-random/rand", + "homepage": "https://rust-random.github.io/book", + "documentation": "https://docs.rs/rand_core", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex", + "version": "1.7.1", + "id": "regex 1.7.1 (path+file:///$ROOT$regex)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", + "source": null, + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.18", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": null, + "req": "^0.6.27", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex/regex-syntax" + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "getrandom", + "small_rng" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex", + "src_path": "$ROOT$regex/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-bytes", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-cheat", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-cheat.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-replace", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-replace.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single-cheat", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-single-cheat.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-single.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna", + "src_path": "$ROOT$regex/examples/shootout-regex-dna.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default", + "src_path": "$ROOT$regex/tests/test_default.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default-bytes", + "src_path": "$ROOT$regex/tests/test_default_bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa", + "src_path": "$ROOT$regex/tests/test_nfa.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-utf8bytes", + "src_path": "$ROOT$regex/tests/test_nfa_utf8bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-bytes", + "src_path": "$ROOT$regex/tests/test_nfa_bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack", + "src_path": "$ROOT$regex/tests/test_backtrack.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-utf8bytes", + "src_path": "$ROOT$regex/tests/test_backtrack_utf8bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-bytes", + "src_path": "$ROOT$regex/tests/test_backtrack_bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "crates-regex", + "src_path": "$ROOT$regex/tests/test_crates_regex.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "aho-corasick": [ + "dep:aho-corasick" + ], + "default": [ + "std", + "perf", + "unicode", + "regex-syntax/default" + ], + "memchr": [ + "dep:memchr" + ], + "pattern": [], + "perf": [ + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal" + ], + "perf-cache": [], + "perf-dfa": [], + "perf-inline": [], + "perf-literal": [ + "aho-corasick", + "memchr" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment", + "regex-syntax/unicode" + ], + "unicode-age": [ + "regex-syntax/unicode-age" + ], + "unicode-bool": [ + "regex-syntax/unicode-bool" + ], + "unicode-case": [ + "regex-syntax/unicode-case" + ], + "unicode-gencat": [ + "regex-syntax/unicode-gencat" + ], + "unicode-perl": [ + "regex-syntax/unicode-perl" + ], + "unicode-script": [ + "regex-syntax/unicode-script" + ], + "unicode-segment": [ + "regex-syntax/unicode-segment" + ], + "unstable": [ + "pattern" + ], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$regex/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "text-processing" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex", + "version": "1.8.1", + "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.5.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "getrandom", + "small_rng" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-cheat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-replace", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single-cheat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-utf8bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-utf8bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "crates-regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "aho-corasick": [ + "dep:aho-corasick" + ], + "default": [ + "std", + "perf", + "unicode", + "regex-syntax/default" + ], + "memchr": [ + "dep:memchr" + ], + "pattern": [], + "perf": [ + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal" + ], + "perf-cache": [], + "perf-dfa": [], + "perf-inline": [], + "perf-literal": [ + "aho-corasick", + "memchr" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment", + "regex-syntax/unicode" + ], + "unicode-age": [ + "regex-syntax/unicode-age" + ], + "unicode-bool": [ + "regex-syntax/unicode-bool" + ], + "unicode-case": [ + "regex-syntax/unicode-case" + ], + "unicode-gencat": [ + "regex-syntax/unicode-gencat" + ], + "unicode-perl": [ + "regex-syntax/unicode-perl" + ], + "unicode-script": [ + "regex-syntax/unicode-script" + ], + "unicode-segment": [ + "regex-syntax/unicode-segment" + ], + "unstable": [ + "pattern" + ], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "text-processing" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "regex-benchmark", + "version": "0.1.0", + "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Regex benchmarks for Rust's and other engines.", + "source": null, + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "docopt", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libpcre-sys", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memmap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "onig", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": null, + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex" + }, + { + "name": "regex-syntax", + "source": null, + "req": "^0.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex/regex-syntax" + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "cc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "pkg-config", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.9", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "regex-run-one", + "src_path": "$ROOT$regex/bench/src/main.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$regex/bench/src/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$regex/bench/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "libpcre-sys": [ + "dep:libpcre-sys" + ], + "onig": [ + "dep:onig" + ], + "re-onig": [ + "onig" + ], + "re-pcre1": [ + "libpcre-sys" + ], + "re-pcre2": [], + "re-re2": [], + "re-rust": [], + "re-rust-bytes": [], + "re-tcl": [] + }, + "manifest_path": "$ROOT$regex/bench/Cargo.toml", + "metadata": null, + "publish": [], + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": null, + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-debug", + "version": "0.1.0", + "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A tool useful for debugging regular expressions.", + "source": null, + "dependencies": [ + { + "name": "docopt", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": null, + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex" + }, + { + "name": "regex-syntax", + "source": null, + "req": "^0.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex/regex-syntax" + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "regex-debug", + "src_path": "$ROOT$regex/regex-debug/src/main.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$regex/regex-debug/Cargo.toml", + "metadata": null, + "publish": [], + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": null, + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-syntax", + "version": "0.6.28", + "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A regular expression parser.", + "source": null, + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-syntax", + "src_path": "$ROOT$regex/regex-syntax/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$regex/regex-syntax/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "unicode" + ], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ], + "unicode-age": [], + "unicode-bool": [], + "unicode-case": [], + "unicode-gencat": [], + "unicode-perl": [], + "unicode-script": [], + "unicode-segment": [] + }, + "manifest_path": "$ROOT$regex/regex-syntax/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex-syntax", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-syntax", + "version": "0.7.1", + "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A regular expression parser.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-syntax", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "std", + "unicode" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ], + "unicode-age": [], + "unicode-bool": [], + "unicode-case": [], + "unicode-gencat": [], + "unicode-perl": [], + "unicode-script": [], + "unicode-segment": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex-syntax", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "rure", + "version": "0.2.2", + "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A C API for Rust's regular expression library.\n", + "source": null, + "dependencies": [ + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": null, + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex" + } + ], + "targets": [ + { + "kind": [ + "staticlib", + "cdylib", + "rlib" + ], + "crate_types": [ + "staticlib", + "cdylib", + "rlib" + ], + "name": "rure", + "src_path": "$ROOT$regex/regex-capi/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$regex/regex-capi/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://github.com/rust-lang/regex/tree/master/regex-capi", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "serde", + "version": "1.0.160", + "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A generic serialization/deserialization framework", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.160", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "serde", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [], + "default": [ + "std" + ], + "derive": [ + "serde_derive" + ], + "rc": [], + "serde_derive": [ + "dep:serde_derive" + ], + "std": [], + "unstable": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "derive" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "derive", + "rc" + ] + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar ", + "David Tolnay " + ], + "categories": [ + "encoding", + "no-std" + ], + "keywords": [ + "serde", + "serialization", + "no_std" + ], + "readme": "crates-io.md", + "repository": "https://github.com/serde-rs/serde", + "homepage": "https://serde.rs", + "documentation": "https://docs.rs/serde", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": "1.19" + }, + { + "name": "serde_derive", + "version": "1.0.160", + "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "syn", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "proc-macro" + ], + "crate_types": [ + "proc-macro" + ], + "name": "serde_derive", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [], + "deserialize_in_place": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar ", + "David Tolnay " + ], + "categories": [ + "no-std" + ], + "keywords": [ + "serde", + "serialization", + "no_std", + "derive" + ], + "readme": "crates-io.md", + "repository": "https://github.com/serde-rs/serde", + "homepage": "https://serde.rs", + "documentation": "https://serde.rs/derive.html", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "strsim", + "version": "0.10.0", + "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "Implementations of string similarity metrics. Includes Hamming, Levenshtein,\nOSA, Damerau-Levenshtein, Jaro, Jaro-Winkler, and Sørensen-Dice.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "strsim", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "lib", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/tests/lib.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "benches", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/benches/benches.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Danny Guo " + ], + "categories": [], + "keywords": [ + "string", + "similarity", + "Hamming", + "Levenshtein", + "Jaro" + ], + "readme": "README.md", + "repository": "https://github.com/dguo/strsim-rs", + "homepage": "https://github.com/dguo/strsim-rs", + "documentation": "https://docs.rs/strsim/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "syn", + "version": "2.0.15", + "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Parser for Rust source code", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.55", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.25", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-ident", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "anyhow", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "automod", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "flate2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "insta", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rayon", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ref-cast", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "reqwest", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "blocking" + ], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "syn-test-suite", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tar", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.16", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.3.2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "syn", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "regression", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_asyncness", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_attribute", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_derive_input", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_expr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_generics", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_grouping", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_ident", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_item", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_iterators", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_lit", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_meta", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_parse_buffer", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_parse_stream", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_pat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_path", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_precedence", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_receiver", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_round_trip", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_shebang", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_should_parse", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_stmt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_token_trees", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_ty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_visibility", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "zzz_stable", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "rust", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs", + "edition": "2021", + "required-features": [ + "full", + "parsing" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "file", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs", + "edition": "2021", + "required-features": [ + "full", + "parsing" + ], + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "clone-impls": [], + "default": [ + "derive", + "parsing", + "printing", + "clone-impls", + "proc-macro" + ], + "derive": [], + "extra-traits": [], + "fold": [], + "full": [], + "parsing": [], + "printing": [ + "quote" + ], + "proc-macro": [ + "proc-macro2/proc-macro", + "quote/proc-macro" + ], + "quote": [ + "dep:quote" + ], + "test": [ + "syn-test-suite/all-features" + ], + "visit": [], + "visit-mut": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "doc_cfg" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "full", + "visit", + "visit-mut", + "fold", + "extra-traits" + ] + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "development-tools::procedural-macro-helpers", + "parser-implementations" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/syn", + "homepage": null, + "documentation": "https://docs.rs/syn", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "unicode-ident", + "version": "1.0.8", + "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016", + "license_file": null, + "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "fst", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "small_rng" + ], + "target": null, + "registry": null + }, + { + "name": "roaring", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.10", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ucd-trie", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-xid", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "unicode-ident", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compare", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "static_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "xid", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "development-tools::procedural-macro-helpers", + "no-std" + ], + "keywords": [ + "unicode", + "xid" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/unicode-ident", + "homepage": null, + "documentation": "https://docs.rs/unicode-ident", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "wasi", + "version": "0.11.0+wasi-snapshot-preview1", + "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "license_file": null, + "description": "Experimental WASI API bindings for Rust", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-alloc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "wasi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [ + "std" + ], + "rustc-dep-of-std": [ + "compiler_builtins", + "core", + "rustc-std-workspace-alloc" + ], + "rustc-std-workspace-alloc": [ + "dep:rustc-std-workspace-alloc" + ], + "std": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Cranelift Project Developers" + ], + "categories": [ + "no-std", + "wasm" + ], + "keywords": [ + "webassembly", + "wasm" + ], + "readme": "README.md", + "repository": "https://github.com/bytecodealliance/wasi", + "homepage": null, + "documentation": "https://docs.rs/wasi", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi", + "version": "0.3.9", + "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Raw FFI bindings for all of Windows API.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "winapi-i686-pc-windows-gnu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "i686-pc-windows-gnu", + "registry": null + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "x86_64-pc-windows-gnu", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "accctrl": [], + "aclapi": [], + "activation": [], + "adhoc": [], + "appmgmt": [], + "audioclient": [], + "audiosessiontypes": [], + "avrt": [], + "basetsd": [], + "bcrypt": [], + "bits": [], + "bits10_1": [], + "bits1_5": [], + "bits2_0": [], + "bits2_5": [], + "bits3_0": [], + "bits4_0": [], + "bits5_0": [], + "bitscfg": [], + "bitsmsg": [], + "bluetoothapis": [], + "bluetoothleapis": [], + "bthdef": [], + "bthioctl": [], + "bthledef": [], + "bthsdpdef": [], + "bugcodes": [], + "cderr": [], + "cfg": [], + "cfgmgr32": [], + "cguid": [], + "combaseapi": [], + "coml2api": [], + "commapi": [], + "commctrl": [], + "commdlg": [], + "commoncontrols": [], + "consoleapi": [], + "corecrt": [], + "corsym": [], + "d2d1": [], + "d2d1_1": [], + "d2d1_2": [], + "d2d1_3": [], + "d2d1effectauthor": [], + "d2d1effects": [], + "d2d1effects_1": [], + "d2d1effects_2": [], + "d2d1svg": [], + "d2dbasetypes": [], + "d3d": [], + "d3d10": [], + "d3d10_1": [], + "d3d10_1shader": [], + "d3d10effect": [], + "d3d10misc": [], + "d3d10sdklayers": [], + "d3d10shader": [], + "d3d11": [], + "d3d11_1": [], + "d3d11_2": [], + "d3d11_3": [], + "d3d11_4": [], + "d3d11on12": [], + "d3d11sdklayers": [], + "d3d11shader": [], + "d3d11tokenizedprogramformat": [], + "d3d12": [], + "d3d12sdklayers": [], + "d3d12shader": [], + "d3d9": [], + "d3d9caps": [], + "d3d9types": [], + "d3dcommon": [], + "d3dcompiler": [], + "d3dcsx": [], + "d3dkmdt": [], + "d3dkmthk": [], + "d3dukmdt": [], + "d3dx10core": [], + "d3dx10math": [], + "d3dx10mesh": [], + "datetimeapi": [], + "davclnt": [], + "dbghelp": [], + "dbt": [], + "dcommon": [], + "dcomp": [], + "dcompanimation": [], + "dcomptypes": [], + "dde": [], + "ddraw": [], + "ddrawi": [], + "ddrawint": [], + "debug": [ + "impl-debug" + ], + "debugapi": [], + "devguid": [], + "devicetopology": [], + "devpkey": [], + "devpropdef": [], + "dinput": [], + "dinputd": [], + "dispex": [], + "dmksctl": [], + "dmusicc": [], + "docobj": [], + "documenttarget": [], + "dot1x": [], + "dpa_dsa": [], + "dpapi": [], + "dsgetdc": [], + "dsound": [], + "dsrole": [], + "dvp": [], + "dwmapi": [], + "dwrite": [], + "dwrite_1": [], + "dwrite_2": [], + "dwrite_3": [], + "dxdiag": [], + "dxfile": [], + "dxgi": [], + "dxgi1_2": [], + "dxgi1_3": [], + "dxgi1_4": [], + "dxgi1_5": [], + "dxgi1_6": [], + "dxgidebug": [], + "dxgiformat": [], + "dxgitype": [], + "dxva2api": [], + "dxvahd": [], + "eaptypes": [], + "enclaveapi": [], + "endpointvolume": [], + "errhandlingapi": [], + "everything": [], + "evntcons": [], + "evntprov": [], + "evntrace": [], + "excpt": [], + "exdisp": [], + "fibersapi": [], + "fileapi": [], + "functiondiscoverykeys_devpkey": [], + "gl-gl": [], + "guiddef": [], + "handleapi": [], + "heapapi": [], + "hidclass": [], + "hidpi": [], + "hidsdi": [], + "hidusage": [], + "highlevelmonitorconfigurationapi": [], + "hstring": [], + "http": [], + "ifdef": [], + "ifmib": [], + "imm": [], + "impl-debug": [], + "impl-default": [], + "in6addr": [], + "inaddr": [], + "inspectable": [], + "interlockedapi": [], + "intsafe": [], + "ioapiset": [], + "ipexport": [], + "iphlpapi": [], + "ipifcons": [], + "ipmib": [], + "iprtrmib": [], + "iptypes": [], + "jobapi": [], + "jobapi2": [], + "knownfolders": [], + "ks": [], + "ksmedia": [], + "ktmtypes": [], + "ktmw32": [], + "l2cmn": [], + "libloaderapi": [], + "limits": [], + "lmaccess": [], + "lmalert": [], + "lmapibuf": [], + "lmat": [], + "lmcons": [], + "lmdfs": [], + "lmerrlog": [], + "lmjoin": [], + "lmmsg": [], + "lmremutl": [], + "lmrepl": [], + "lmserver": [], + "lmshare": [], + "lmstats": [], + "lmsvc": [], + "lmuse": [], + "lmwksta": [], + "lowlevelmonitorconfigurationapi": [], + "lsalookup": [], + "memoryapi": [], + "minschannel": [], + "minwinbase": [], + "minwindef": [], + "mmdeviceapi": [], + "mmeapi": [], + "mmreg": [], + "mmsystem": [], + "mprapidef": [], + "msaatext": [], + "mscat": [], + "mschapp": [], + "mssip": [], + "mstcpip": [], + "mswsock": [], + "mswsockdef": [], + "namedpipeapi": [], + "namespaceapi": [], + "nb30": [], + "ncrypt": [], + "netioapi": [], + "nldef": [], + "ntddndis": [], + "ntddscsi": [], + "ntddser": [], + "ntdef": [], + "ntlsa": [], + "ntsecapi": [], + "ntstatus": [], + "oaidl": [], + "objbase": [], + "objidl": [], + "objidlbase": [], + "ocidl": [], + "ole2": [], + "oleauto": [], + "olectl": [], + "oleidl": [], + "opmapi": [], + "pdh": [], + "perflib": [], + "physicalmonitorenumerationapi": [], + "playsoundapi": [], + "portabledevice": [], + "portabledeviceapi": [], + "portabledevicetypes": [], + "powerbase": [], + "powersetting": [], + "powrprof": [], + "processenv": [], + "processsnapshot": [], + "processthreadsapi": [], + "processtopologyapi": [], + "profileapi": [], + "propidl": [], + "propkey": [], + "propkeydef": [], + "propsys": [], + "prsht": [], + "psapi": [], + "qos": [], + "realtimeapiset": [], + "reason": [], + "restartmanager": [], + "restrictederrorinfo": [], + "rmxfguid": [], + "roapi": [], + "robuffer": [], + "roerrorapi": [], + "rpc": [], + "rpcdce": [], + "rpcndr": [], + "rtinfo": [], + "sapi": [], + "sapi51": [], + "sapi53": [], + "sapiddk": [], + "sapiddk51": [], + "schannel": [], + "sddl": [], + "securityappcontainer": [], + "securitybaseapi": [], + "servprov": [], + "setupapi": [], + "shellapi": [], + "shellscalingapi": [], + "shlobj": [], + "shobjidl": [], + "shobjidl_core": [], + "shtypes": [], + "softpub": [], + "spapidef": [], + "spellcheck": [], + "sporder": [], + "sql": [], + "sqlext": [], + "sqltypes": [], + "sqlucode": [], + "sspi": [], + "std": [], + "stralign": [], + "stringapiset": [], + "strmif": [], + "subauth": [], + "synchapi": [], + "sysinfoapi": [], + "systemtopologyapi": [], + "taskschd": [], + "tcpestats": [], + "tcpmib": [], + "textstor": [], + "threadpoolapiset": [], + "threadpoollegacyapiset": [], + "timeapi": [], + "timezoneapi": [], + "tlhelp32": [], + "transportsettingcommon": [], + "tvout": [], + "udpmib": [], + "unknwnbase": [], + "urlhist": [], + "urlmon": [], + "usb": [], + "usbioctl": [], + "usbiodef": [], + "usbscan": [], + "usbspec": [], + "userenv": [], + "usp10": [], + "utilapiset": [], + "uxtheme": [], + "vadefs": [], + "vcruntime": [], + "vsbackup": [], + "vss": [], + "vsserror": [], + "vswriter": [], + "wbemads": [], + "wbemcli": [], + "wbemdisp": [], + "wbemprov": [], + "wbemtran": [], + "wct": [], + "werapi": [], + "winbase": [], + "wincodec": [], + "wincodecsdk": [], + "wincon": [], + "wincontypes": [], + "wincred": [], + "wincrypt": [], + "windef": [], + "windot11": [], + "windowsceip": [], + "windowsx": [], + "winefs": [], + "winerror": [], + "winevt": [], + "wingdi": [], + "winhttp": [], + "wininet": [], + "winineti": [], + "winioctl": [], + "winnetwk": [], + "winnls": [], + "winnt": [], + "winreg": [], + "winsafer": [], + "winscard": [], + "winsmcrd": [], + "winsock2": [], + "winspool": [], + "winstring": [], + "winsvc": [], + "wintrust": [], + "winusb": [], + "winusbio": [], + "winuser": [], + "winver": [], + "wlanapi": [], + "wlanihv": [], + "wlanihvtypes": [], + "wlantypes": [], + "wlclient": [], + "wmistr": [], + "wnnc": [], + "wow64apiset": [], + "wpdmtpextensions": [], + "ws2bth": [], + "ws2def": [], + "ws2ipdef": [], + "ws2spi": [], + "ws2tcpip": [], + "wtsapi32": [], + "wtypes": [], + "wtypesbase": [], + "xinput": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "default-target": "x86_64-pc-windows-msvc", + "features": [ + "everything", + "impl-debug", + "impl-default" + ], + "targets": [ + "aarch64-pc-windows-msvc", + "i686-pc-windows-msvc", + "x86_64-pc-windows-msvc" + ] + } + } + }, + "publish": null, + "authors": [ + "Peter Atashian " + ], + "categories": [ + "external-ffi-bindings", + "no-std", + "os::windows-apis" + ], + "keywords": [ + "windows", + "ffi", + "win32", + "com", + "directx" + ], + "readme": "README.md", + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": "https://docs.rs/winapi/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-i686-pc-windows-gnu", + "version": "0.4.0", + "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-i686-pc-windows-gnu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Peter Atashian " + ], + "categories": [], + "keywords": [ + "windows" + ], + "readme": null, + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "version": "0.4.0", + "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-x86_64-pc-windows-gnu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Peter Atashian " + ], + "categories": [], + "keywords": [ + "windows" + ], + "readme": null, + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + } + ], + "workspace_members": [ + "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", + "regex 1.7.1 (path+file:///$ROOT$regex)", + "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", + "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)" + ], + "resolve": { + "nodes": [ + { + "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "std" + ] + }, + { + "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "strsim", + "pkg": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + }, + { + "name": "wasi", + "pkg": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(target_os = \"wasi\")" + } + ] + } + ], + "features": [] + }, + { + "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + }, + { + "name": "winapi", + "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "unicode_ident", + "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "proc-macro" + ] + }, + { + "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "rand", + "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "proc-macro" + ] + }, + { + "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "rand_core", + "pkg": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "getrandom", + "small_rng" + ] + }, + { + "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "getrandom", + "pkg": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "getrandom" + ] + }, + { + "id": "regex 1.7.1 (path+file:///$ROOT$regex)", + "dependencies": [ + "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)" + ], + "deps": [ + { + "name": "aho_corasick", + "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quickcheck", + "pkg": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "rand", + "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "aho-corasick", + "default", + "memchr", + "perf", + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal", + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", + "dependencies": [ + "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.7.1 (path+file:///$ROOT$regex)", + "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cc", + "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "cfg_if", + "pkg": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "docopt", + "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "memmap", + "pkg": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "pkg_config", + "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)", + "dependencies": [ + "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.7.1 (path+file:///$ROOT$regex)", + "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "docopt", + "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.7.1 (path+file:///$ROOT$regex)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "serde_derive", + "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "derive", + "serde_derive", + "std" + ] + }, + { + "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quote", + "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "syn", + "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default" + ] + }, + { + "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quote", + "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "unicode_ident", + "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "clone-impls", + "default", + "derive", + "parsing", + "printing", + "proc-macro", + "quote" + ] + }, + { + "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi_i686_pc_windows_gnu", + "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "i686-pc-windows-gnu" + } + ] + }, + { + "name": "winapi_x86_64_pc_windows_gnu", + "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "x86_64-pc-windows-gnu" + } + ] + } + ], + "features": [ + "basetsd", + "handleapi", + "memoryapi", + "minwindef", + "std", + "sysinfoapi" + ] + }, + { + "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + } + ], + "root": "regex 1.7.1 (path+file:///$ROOT$regex)" + }, + "target_directory": "$ROOT$regex/target", + "version": 1, + "workspace_root": "$ROOT$regex", + "metadata": null +} diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json new file mode 100644 index 000000000000..131ff5dd7157 --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json @@ -0,0 +1,12816 @@ +{ + "packages": [ + { + "name": "aho-corasick", + "version": "0.7.20", + "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast multiple substring searching.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "aho_corasick", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "default": [ + "std" + ], + "std": [ + "memchr/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "string", + "search", + "text", + "aho", + "multi" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/aho-corasick", + "homepage": "https://github.com/BurntSushi/aho-corasick", + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "aho-corasick", + "version": "1.0.1", + "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast multiple substring searching.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.17", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "aho_corasick", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "default": [ + "std", + "perf-literal" + ], + "logging": [ + "dep:log" + ], + "perf-literal": [ + "dep:memchr" + ], + "std": [ + "memchr?/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "string", + "search", + "text", + "pattern", + "multi" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/aho-corasick", + "homepage": "https://github.com/BurntSushi/aho-corasick", + "documentation": null, + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "atty", + "version": "0.2.14", + "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "A simple interface for querying atty", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "hermit-abi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(target_os = \"hermit\")", + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": "cfg(unix)", + "registry": null + }, + { + "name": "winapi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "consoleapi", + "processenv", + "minwinbase", + "minwindef", + "winbase" + ], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "atty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "atty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/examples/atty.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "softprops " + ], + "categories": [], + "keywords": [ + "terminal", + "tty", + "isatty" + ], + "readme": "README.md", + "repository": "https://github.com/softprops/atty", + "homepage": "https://github.com/softprops/atty", + "documentation": "http://softprops.github.io/atty", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "base64", + "version": "0.20.0", + "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "encodes and decodes base64 as bytes or utf8", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.5", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "small_rng" + ], + "target": null, + "registry": null + }, + { + "name": "rstest", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.12.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rstest_reuse", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "structopt", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.26", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "base64", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "base64", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/examples/base64.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "encode", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/encode.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "tests", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/tests.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "benchmarks", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/benches/benchmarks.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [], + "default": [ + "std" + ], + "std": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alice Maz ", + "Marshall Pierce " + ], + "categories": [ + "encoding" + ], + "keywords": [ + "base64", + "utf8", + "encode", + "decode", + "no_std" + ], + "readme": "README.md", + "repository": "https://github.com/marshallpierce/rust-base64", + "homepage": null, + "documentation": "https://docs.rs/base64", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.57.0" + }, + { + "name": "bitflags", + "version": "1.3.2", + "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro to generate structures which behave like bitflags.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "trybuild", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "bitflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "basic", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/basic.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compile", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/compile.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [], + "example_generated": [], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "example_generated" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "no-std" + ], + "keywords": [ + "bit", + "bitmask", + "bitflags", + "flags" + ], + "readme": "README.md", + "repository": "https://github.com/bitflags/bitflags", + "homepage": "https://github.com/bitflags/bitflags", + "documentation": "https://docs.rs/bitflags", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "bstr", + "version": "1.4.0", + "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A string type that is not required to be valid UTF-8.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "once_cell", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.14.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-automata", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.85", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ucd-parse", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-segmentation", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.2.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "bstr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "graphemes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes.rs", + "edition": "2021", + "required-features": [ + "std", + "unicode" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "lines", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "uppercase", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase.rs", + "edition": "2021", + "required-features": [ + "std", + "unicode" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "words", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words.rs", + "edition": "2021", + "required-features": [ + "std", + "unicode" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "graphemes-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes-std.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "lines-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines-std.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "uppercase-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase-std.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "words-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words-std.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [ + "serde?/alloc" + ], + "default": [ + "std", + "unicode" + ], + "serde": [ + "dep:serde" + ], + "std": [ + "alloc", + "memchr/std", + "serde?/std" + ], + "unicode": [ + "dep:once_cell", + "dep:regex-automata" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing", + "encoding" + ], + "keywords": [ + "string", + "str", + "byte", + "bytes", + "text" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/bstr", + "homepage": "https://github.com/BurntSushi/bstr", + "documentation": "https://docs.rs/bstr", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60" + }, + { + "name": "bytecount", + "version": "0.6.3", + "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Apache-2.0/MIT", + "license_file": null, + "description": "count occurrences of a given byte, or the number of UTF-8 code points, in a byte slice, fast", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "packed_simd_2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.8", + "kind": null, + "rename": "packed_simd", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "bytecount", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "check", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/tests/check.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "generic-simd": [ + "packed_simd" + ], + "html_report": [], + "packed_simd": [ + "dep:packed_simd" + ], + "runtime-dispatch-simd": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andre Bogus ", + "Joshua Landau " + ], + "categories": [ + "algorithms", + "no-std" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/llogiq/bytecount", + "homepage": null, + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cc", + "version": "1.0.79", + "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "jobserver", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.16", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempfile", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "gcc-shim", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cc_env", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cxxflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "jobserver": [ + "dep:jobserver" + ], + "parallel": [ + "jobserver" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [ + "development-tools::build-utils" + ], + "keywords": [ + "build-dependencies" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/cc-rs", + "homepage": "https://github.com/rust-lang/cc-rs", + "documentation": "https://docs.rs/cc", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cfg-if", + "version": "1.0.0", + "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cfg-if", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "xcrate", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/alexcrichton/cfg-if", + "homepage": "https://github.com/alexcrichton/cfg-if", + "documentation": "https://docs.rs/cfg-if", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "clap", + "version": "2.34.0", + "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "A simple to use, efficient, and full-featured Command Line Argument Parser\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "atty", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bitflags", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "clippy", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "~0.0.166", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "strsim", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "term_size", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "textwrap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-width", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "vec_map", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "yaml-rust", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "version-sync", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ansi_term", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.12", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": "cfg(not(windows))", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "clap", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "ansi_term": [ + "dep:ansi_term" + ], + "atty": [ + "dep:atty" + ], + "clippy": [ + "dep:clippy" + ], + "color": [ + "ansi_term", + "atty" + ], + "debug": [], + "default": [ + "suggestions", + "color", + "vec_map" + ], + "doc": [ + "yaml" + ], + "nightly": [], + "no_cargo": [], + "strsim": [ + "dep:strsim" + ], + "suggestions": [ + "strsim" + ], + "term_size": [ + "dep:term_size" + ], + "unstable": [], + "vec_map": [ + "dep:vec_map" + ], + "wrap_help": [ + "term_size", + "textwrap/term_size" + ], + "yaml": [ + "yaml-rust" + ], + "yaml-rust": [ + "dep:yaml-rust" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "doc" + ] + } + } + }, + "publish": null, + "authors": [ + "Kevin K. " + ], + "categories": [ + "command-line-interface" + ], + "keywords": [ + "argument", + "cli", + "arg", + "parser", + "parse" + ], + "readme": "README.md", + "repository": "https://github.com/clap-rs/clap", + "homepage": "https://clap.rs/", + "documentation": "https://docs.rs/clap/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "crossbeam-channel", + "version": "0.5.8", + "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Multi-producer multi-consumer channels for message passing", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "crossbeam-utils", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "num_cpus", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.13.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "signal-hook", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "crossbeam-channel", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "fibonacci", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/fibonacci.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "matching", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/matching.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "stopwatch", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/stopwatch.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "after", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/after.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "array", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/array.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "golang", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/golang.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "iter", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/iter.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "list", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/list.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "mpsc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/mpsc.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "never", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/never.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "ready", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/ready.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "same_channel", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/same_channel.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "select", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "select_macro", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select_macro.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "thread_locals", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/thread_locals.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "tick", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/tick.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "zero", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/zero.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "crossbeam", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/benches/crossbeam.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "crossbeam-utils": [ + "dep:crossbeam-utils" + ], + "default": [ + "std" + ], + "std": [ + "crossbeam-utils/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [ + "algorithms", + "concurrency", + "data-structures" + ], + "keywords": [ + "channel", + "mpmc", + "select", + "golang", + "message" + ], + "readme": "README.md", + "repository": "https://github.com/crossbeam-rs/crossbeam", + "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel", + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.38" + }, + { + "name": "crossbeam-utils", + "version": "0.8.15", + "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Utilities for concurrent programming", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "loom", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": "cfg(crossbeam_loom)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "crossbeam-utils", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "atomic_cell", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/atomic_cell.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cache_padded", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/cache_padded.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "parker", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/parker.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "sharded_lock", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/sharded_lock.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "thread", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/thread.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "wait_group", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/wait_group.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "atomic_cell", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/benches/atomic_cell.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "std" + ], + "loom": [ + "dep:loom" + ], + "nightly": [], + "std": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [ + "algorithms", + "concurrency", + "data-structures", + "no-std" + ], + "keywords": [ + "scoped", + "thread", + "atomic", + "cache" + ], + "readme": "README.md", + "repository": "https://github.com/crossbeam-rs/crossbeam", + "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils", + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.38" + }, + { + "name": "encoding_rs", + "version": "0.8.32", + "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "(Apache-2.0 OR MIT) AND BSD-3-Clause", + "license_file": null, + "description": "A Gecko-oriented implementation of the Encoding Standard", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "packed_simd_2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.4", + "kind": null, + "rename": "packed_simd", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bincode", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "encoding_rs", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "alloc": [], + "default": [ + "alloc" + ], + "fast-big5-hanzi-encode": [], + "fast-gb-hanzi-encode": [], + "fast-hangul-encode": [], + "fast-hanja-encode": [], + "fast-kanji-encode": [], + "fast-legacy-encode": [ + "fast-hangul-encode", + "fast-hanja-encode", + "fast-kanji-encode", + "fast-gb-hanzi-encode", + "fast-big5-hanzi-encode" + ], + "less-slow-big5-hanzi-encode": [], + "less-slow-gb-hanzi-encode": [], + "less-slow-kanji-encode": [], + "packed_simd": [ + "dep:packed_simd" + ], + "serde": [ + "dep:serde" + ], + "simd-accel": [ + "packed_simd", + "packed_simd/into_bits" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Henri Sivonen " + ], + "categories": [ + "text-processing", + "encoding", + "web-programming", + "internationalization" + ], + "keywords": [ + "encoding", + "web", + "unicode", + "charset" + ], + "readme": "README.md", + "repository": "https://github.com/hsivonen/encoding_rs", + "homepage": "https://docs.rs/encoding_rs/", + "documentation": "https://docs.rs/encoding_rs/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "encoding_rs_io", + "version": "0.1.7", + "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Streaming transcoding for encoding_rs", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "encoding_rs", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "encoding_rs_io", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing", + "encoding", + "web-programming", + "email" + ], + "keywords": [ + "encoding", + "transcoding", + "stream", + "io", + "read" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/encoding_rs_io", + "homepage": null, + "documentation": "https://docs.rs/encoding_rs_io", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "fnv", + "version": "1.0.7", + "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Apache-2.0 / MIT", + "license_file": null, + "description": "Fowler–Noll–Vo hash function", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "fnv", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "default": [ + "std" + ], + "std": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/servo/rust-fnv", + "homepage": null, + "documentation": "https://doc.servo.org/fnv/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "glob", + "version": "0.3.1", + "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Support for matching file paths against Unix shell style patterns.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "glob", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "glob-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/tests/glob-std.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "filesystem" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/glob", + "homepage": "https://github.com/rust-lang/glob", + "documentation": "https://docs.rs/glob/0.3.1", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "globset", + "version": "0.4.10", + "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Cross platform single glob and glob set matching. Glob set matching is the\nprocess of matching one or more glob patterns against a single candidate path\nsimultaneously, and returning all of the globs that matched.\n", + "source": null, + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "fnv", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "perf", + "std" + ], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.104", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "glob", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.45", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "globset", + "src_path": "$ROOT$ripgrep/crates/globset/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$ripgrep/crates/globset/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "log" + ], + "log": [ + "dep:log" + ], + "serde": [ + "dep:serde" + ], + "serde1": [ + "serde" + ], + "simd-accel": [] + }, + "manifest_path": "$ROOT$ripgrep/crates/globset/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "glob", + "multiple", + "set", + "pattern" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset", + "documentation": "https://docs.rs/globset", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep", + "version": "0.2.11", + "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast line oriented regex searching as a library.\n", + "source": null, + "dependencies": [ + { + "name": "grep-cli", + "source": null, + "req": "^0.1.7", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/cli" + }, + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "grep-pcre2", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/pcre2" + }, + { + "name": "grep-printer", + "source": null, + "req": "^0.1.7", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/printer" + }, + { + "name": "grep-regex", + "source": null, + "req": "^0.1.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/regex" + }, + { + "name": "grep-searcher", + "source": null, + "req": "^0.1.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/searcher" + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.2.7", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep", + "src_path": "$ROOT$ripgrep/crates/grep/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "simplegrep", + "src_path": "$ROOT$ripgrep/crates/grep/examples/simplegrep.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "avx-accel": [], + "grep-pcre2": [ + "dep:grep-pcre2" + ], + "pcre2": [ + "grep-pcre2" + ], + "simd-accel": [ + "grep-searcher/simd-accel" + ] + }, + "manifest_path": "$ROOT$ripgrep/crates/grep/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "egrep", + "search", + "pattern" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep", + "documentation": "https://docs.rs/grep", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-cli", + "version": "0.1.7", + "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Utilities for search oriented command line applications.\n", + "source": null, + "dependencies": [ + { + "name": "atty", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "globset", + "source": null, + "req": "^0.4.10", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/globset" + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "same-file", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-cli", + "src_path": "$ROOT$ripgrep/crates/cli/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$ripgrep/crates/cli/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "cli", + "utility", + "util" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli", + "documentation": "https://docs.rs/grep-cli", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-matcher", + "version": "0.1.6", + "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "A trait for regular expressions, with a focus on line oriented search.\n", + "source": null, + "dependencies": [ + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-matcher", + "src_path": "$ROOT$ripgrep/crates/matcher/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "integration", + "src_path": "$ROOT$ripgrep/crates/matcher/tests/tests.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$ripgrep/crates/matcher/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "pattern", + "trait" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher", + "documentation": "https://docs.rs/grep-matcher", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-pcre2", + "version": "0.1.6", + "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Use PCRE2 with the 'grep' crate.\n", + "source": null, + "dependencies": [ + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "pcre2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-pcre2", + "src_path": "$ROOT$ripgrep/crates/pcre2/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$ripgrep/crates/pcre2/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "pcre", + "backreference", + "look" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2", + "documentation": "https://docs.rs/grep-pcre2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-printer", + "version": "0.1.7", + "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "An implementation of the grep crate's Sink trait that provides standard\nprinting of search results, similar to grep itself.\n", + "source": null, + "dependencies": [ + { + "name": "base64", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.20.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "grep-searcher", + "source": null, + "req": "^0.1.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/searcher" + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.77", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.27", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-regex", + "source": null, + "req": "^0.1.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/regex" + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-printer", + "src_path": "$ROOT$ripgrep/crates/printer/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "base64": [ + "dep:base64" + ], + "default": [ + "serde1" + ], + "serde": [ + "dep:serde" + ], + "serde1": [ + "base64", + "serde", + "serde_json" + ], + "serde_json": [ + "dep:serde_json" + ] + }, + "manifest_path": "$ROOT$ripgrep/crates/printer/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "grep", + "pattern", + "print", + "printer", + "sink" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer", + "documentation": "https://docs.rs/grep-printer", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-regex", + "version": "0.1.11", + "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Use Rust's regex library with the 'grep' crate.\n", + "source": null, + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "thread_local", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-regex", + "src_path": "$ROOT$ripgrep/crates/regex/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$ripgrep/crates/regex/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "search", + "pattern", + "line" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex", + "documentation": "https://docs.rs/grep-regex", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-searcher", + "version": "0.1.11", + "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast line oriented regex searching as a library.\n", + "source": null, + "dependencies": [ + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "bytecount", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "encoding_rs", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.14", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "encoding_rs_io", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memmap2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.3", + "kind": null, + "rename": "memmap", + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-regex", + "source": null, + "req": "^0.1.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/regex" + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-searcher", + "src_path": "$ROOT$ripgrep/crates/searcher/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "search-stdin", + "src_path": "$ROOT$ripgrep/crates/searcher/examples/search-stdin.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "avx-accel": [], + "default": [ + "bytecount/runtime-dispatch-simd" + ], + "simd-accel": [ + "encoding_rs/simd-accel" + ] + }, + "manifest_path": "$ROOT$ripgrep/crates/searcher/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "egrep", + "search", + "pattern" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher", + "documentation": "https://docs.rs/grep-searcher", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "hermit-abi", + "version": "0.1.19", + "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "hermit-abi is small interface to call functions from the unikernel RustyHermit.\nIt is used to build the target `x86_64-unknown-hermit`.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.51", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "hermit-abi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [], + "docs": [], + "rustc-dep-of-std": [ + "core", + "compiler_builtins/rustc-dep-of-std", + "libc/rustc-dep-of-std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "default-target": "x86_64-unknown-hermit", + "features": [ + "docs" + ] + } + } + }, + "publish": null, + "authors": [ + "Stefan Lankes" + ], + "categories": [ + "os" + ], + "keywords": [ + "unikernel", + "libos" + ], + "readme": "README.md", + "repository": "https://github.com/hermitcore/libhermit-rs", + "homepage": null, + "documentation": "https://hermitcore.github.io/rusty-hermit/hermit_abi", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "ignore", + "version": "0.4.20", + "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "A fast library for efficiently matching ignore files such as `.gitignore`\nagainst file paths.\n", + "source": null, + "dependencies": [ + { + "name": "globset", + "source": null, + "req": "^0.4.10", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/globset" + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "same-file", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "thread_local", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.2.7", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "crossbeam-channel", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "ignore", + "src_path": "$ROOT$ripgrep/crates/ignore/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "walk", + "src_path": "$ROOT$ripgrep/crates/ignore/examples/walk.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "gitignore_matched_path_or_any_parents_tests", + "src_path": "$ROOT$ripgrep/crates/ignore/tests/gitignore_matched_path_or_any_parents_tests.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "simd-accel": [ + "globset/simd-accel" + ] + }, + "manifest_path": "$ROOT$ripgrep/crates/ignore/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "glob", + "ignore", + "gitignore", + "pattern", + "file" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore", + "documentation": "https://docs.rs/ignore", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "itoa", + "version": "1.0.6", + "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Fast integer primitive to string conversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "no-panic", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "itoa", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "no-panic": [ + "dep:no-panic" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "value-formatting", + "no-std" + ], + "keywords": [ + "integer" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/itoa", + "homepage": null, + "documentation": "https://docs.rs/itoa", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.36" + }, + { + "name": "jemalloc-sys", + "version": "0.5.3+5.3.0-patched", + "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Rust FFI bindings to jemalloc\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.8", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "cc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.13", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "jemalloc-sys", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "malloc_conf_empty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_empty.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "malloc_conf_set", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_set.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "unprefixed_malloc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/unprefixed_malloc.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "background_threads": [ + "background_threads_runtime_support" + ], + "background_threads_runtime_support": [], + "debug": [], + "default": [ + "background_threads_runtime_support" + ], + "disable_initial_exec_tls": [], + "profiling": [], + "stats": [], + "unprefixed_malloc_on_supported_platforms": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "rustdoc-args": [ + "--cfg", + "jemallocator_docs" + ] + } + } + }, + "publish": null, + "authors": [ + "Alex Crichton ", + "Gonzalo Brito Gadeschi ", + "The TiKV Project Developers" + ], + "categories": [], + "keywords": [ + "allocator", + "jemalloc" + ], + "readme": "README.md", + "repository": "https://github.com/tikv/jemallocator", + "homepage": "https://github.com/tikv/jemallocator", + "documentation": "https://docs.rs/jemallocator-sys", + "edition": "2018", + "links": "jemalloc", + "default_run": null, + "rust_version": null + }, + { + "name": "jemallocator", + "version": "0.5.0", + "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A Rust allocator backed by jemalloc\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "jemalloc-sys", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.8", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "paste", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "jemallocator", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "background_thread_defaults", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_defaults.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "background_thread_enabled", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_enabled.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "ffi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/ffi.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "grow_in_place", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/grow_in_place.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "malloctl", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/malloctl.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "shrink_in_place", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/shrink_in_place.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "smoke", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "smoke_ffi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke_ffi.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "usable_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/usable_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "roundtrip", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/benches/roundtrip.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc_trait": [], + "background_threads": [ + "jemalloc-sys/background_threads" + ], + "background_threads_runtime_support": [ + "jemalloc-sys/background_threads_runtime_support" + ], + "debug": [ + "jemalloc-sys/debug" + ], + "default": [ + "background_threads_runtime_support" + ], + "disable_initial_exec_tls": [ + "jemalloc-sys/disable_initial_exec_tls" + ], + "profiling": [ + "jemalloc-sys/profiling" + ], + "stats": [ + "jemalloc-sys/stats" + ], + "unprefixed_malloc_on_supported_platforms": [ + "jemalloc-sys/unprefixed_malloc_on_supported_platforms" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [], + "rustdoc-args": [ + "--cfg", + "jemallocator_docs" + ] + } + } + }, + "publish": null, + "authors": [ + "Alex Crichton ", + "Gonzalo Brito Gadeschi ", + "Simon Sapin ", + "Steven Fackler ", + "The TiKV Project Developers" + ], + "categories": [ + "memory-management", + "api-bindings" + ], + "keywords": [ + "allocator", + "jemalloc" + ], + "readme": "README.md", + "repository": "https://github.com/tikv/jemallocator", + "homepage": "https://github.com/tikv/jemallocator", + "documentation": "https://docs.rs/jemallocator", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "jobserver", + "version": "0.1.26", + "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "An implementation of the GNU make jobserver for Rust\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "futures", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "num_cpus", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempfile", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tokio-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tokio-process", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.50", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(unix)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "jobserver", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "client", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "server", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/server.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "client-of-myself", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client-of-myself.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "make-as-a-client", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/make-as-a-client.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "helper", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/helper.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/alexcrichton/jobserver-rs", + "homepage": "https://github.com/alexcrichton/jobserver-rs", + "documentation": "https://docs.rs/jobserver", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "lazy_static", + "version": "1.4.0", + "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro for declaring lazily evaluated statics in Rust.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "spin", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "lazy_static", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "no_std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "spin": [ + "dep:spin" + ], + "spin_no_std": [ + "spin" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Marvin Löbel " + ], + "categories": [ + "no-std", + "rust-patterns", + "memory-management" + ], + "keywords": [ + "macro", + "lazy", + "static" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang-nursery/lazy-static.rs", + "homepage": null, + "documentation": "https://docs.rs/lazy_static", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "libc", + "version": "0.2.142", + "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Raw FFI bindings to platform libraries like libc.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "libc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "const_fn", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "align": [], + "const-extern-fn": [], + "default": [ + "std" + ], + "extra_traits": [], + "rustc-dep-of-std": [ + "align", + "rustc-std-workspace-core" + ], + "rustc-std-workspace-core": [ + "dep:rustc-std-workspace-core" + ], + "std": [], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "const-extern-fn", + "extra_traits" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "external-ffi-bindings", + "no-std", + "os" + ], + "keywords": [ + "libc", + "ffi", + "bindings", + "operating", + "system" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/libc", + "homepage": "https://github.com/rust-lang/libc", + "documentation": "https://docs.rs/libc/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "log", + "version": "0.4.17", + "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A lightweight logging facade for Rust\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "sval", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.0-alpha.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "value-bag", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.0-alpha.9", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "serde_test", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "sval", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.0-alpha.5", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "value-bag", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.0-alpha.9", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "test" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "log", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "filters", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/filters.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "macros", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/macros.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "value", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/benches/value.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "kv_unstable": [ + "value-bag" + ], + "kv_unstable_serde": [ + "kv_unstable_std", + "value-bag/serde", + "serde" + ], + "kv_unstable_std": [ + "std", + "kv_unstable", + "value-bag/error" + ], + "kv_unstable_sval": [ + "kv_unstable", + "value-bag/sval", + "sval" + ], + "max_level_debug": [], + "max_level_error": [], + "max_level_info": [], + "max_level_off": [], + "max_level_trace": [], + "max_level_warn": [], + "release_max_level_debug": [], + "release_max_level_error": [], + "release_max_level_info": [], + "release_max_level_off": [], + "release_max_level_trace": [], + "release_max_level_warn": [], + "serde": [ + "dep:serde" + ], + "std": [], + "sval": [ + "dep:sval" + ], + "value-bag": [ + "dep:value-bag" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "std", + "serde", + "kv_unstable_std", + "kv_unstable_sval", + "kv_unstable_serde" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "development-tools::debugging" + ], + "keywords": [ + "logging" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/log", + "homepage": null, + "documentation": "https://docs.rs/log", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "memchr", + "version": "2.5.0", + "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Safe interface to memchr.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.18", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "memchr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [ + "std" + ], + "libc": [ + "dep:libc" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ], + "std": [], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant ", + "bluss" + ], + "categories": [], + "keywords": [ + "memchr", + "char", + "scan", + "strchr", + "string" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/memchr", + "homepage": "https://github.com/BurntSushi/memchr", + "documentation": "https://docs.rs/memchr/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "memmap2", + "version": "0.5.10", + "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Cross-platform Rust API for memory-mapped file IO", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "stable_deref_trait", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "owning_ref", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempfile", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(unix)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "memmap2", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "cat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/examples/cat.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "stable_deref_trait": [ + "dep:stable_deref_trait" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Dan Burkert ", + "Yevhenii Reizner " + ], + "categories": [], + "keywords": [ + "mmap", + "memory-map", + "io", + "file" + ], + "readme": "README.md", + "repository": "https://github.com/RazrFalcon/memmap2-rs", + "homepage": null, + "documentation": "https://docs.rs/memmap2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "once_cell", + "version": "1.17.1", + "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Single assignment cells and lazy values.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "atomic-polyfill", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": "atomic_polyfill", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "critical-section", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": "critical_section", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "parking_lot_core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.9.3", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "critical-section", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.1", + "kind": "dev", + "rename": "critical_section", + "optional": false, + "uses_default_features": true, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "crossbeam-utils", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.7", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.2.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "once_cell", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "bench_acquire", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_acquire.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "bench_vs_lazy_static", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_vs_lazy_static.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "lazy_static", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/lazy_static.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "reentrant_init_deadlocks", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/reentrant_init_deadlocks.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/regex.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "test_synchronization", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/test_synchronization.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "it", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/tests/it.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "alloc": [ + "race" + ], + "atomic-polyfill": [ + "critical-section" + ], + "atomic_polyfill": [ + "dep:atomic_polyfill" + ], + "critical-section": [ + "critical_section", + "atomic_polyfill" + ], + "critical_section": [ + "dep:critical_section" + ], + "default": [ + "std" + ], + "parking_lot": [ + "parking_lot_core" + ], + "parking_lot_core": [ + "dep:parking_lot_core" + ], + "race": [], + "std": [ + "alloc" + ], + "unstable": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true + } + } + }, + "publish": null, + "authors": [ + "Aleksey Kladov " + ], + "categories": [ + "rust-patterns", + "memory-management" + ], + "keywords": [ + "lazy", + "static" + ], + "readme": "README.md", + "repository": "https://github.com/matklad/once_cell", + "homepage": null, + "documentation": "https://docs.rs/once_cell", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "pcre2", + "version": "0.2.3", + "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "High level wrapper library for PCRE2.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.46", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "pcre2-sys", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "thread_local", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "pcre2", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "pcre", + "pcre2", + "regex", + "jit", + "perl" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/rust-pcre2", + "homepage": "https://github.com/BurntSushi/rust-pcre2", + "documentation": "https://docs.rs/pcre2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "pcre2-sys", + "version": "0.2.5", + "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Low level bindings to PCRE2.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "cc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "parallel" + ], + "target": null, + "registry": null + }, + { + "name": "pkg-config", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.13", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "pcre2-sys", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "external-ffi-bindings" + ], + "keywords": [ + "pcre", + "pcre2", + "regex", + "jit" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/rust-pcre2", + "homepage": "https://github.com/BurntSushi/rust-pcre2", + "documentation": "https://docs.rs/pcre2-sys", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "pkg-config", + "version": "0.3.26", + "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "pkg-config", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [ + "build-dependencies" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/pkg-config-rs", + "homepage": null, + "documentation": "https://docs.rs/pkg-config", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "proc-macro2", + "version": "1.0.56", + "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "unicode-ident", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "proc-macro2", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "comments", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "features", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "marker", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_fmt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "proc-macro" + ], + "nightly": [], + "proc-macro": [], + "span-locations": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "rustc-args": [ + "--cfg", + "procmacro2_semver_exempt" + ], + "rustdoc-args": [ + "--cfg", + "procmacro2_semver_exempt", + "--cfg", + "doc_cfg" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "span-locations" + ] + } + }, + "publish": null, + "authors": [ + "David Tolnay ", + "Alex Crichton " + ], + "categories": [ + "development-tools::procedural-macro-helpers" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/proc-macro2", + "homepage": null, + "documentation": "https://docs.rs/proc-macro2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "quote", + "version": "1.0.26", + "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Quasi-quoting macro quote!(...)", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.52", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "trybuild", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.66", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "diff" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "quote", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compiletest", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "proc-macro" + ], + "proc-macro": [ + "proc-macro2/proc-macro" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "development-tools::procedural-macro-helpers" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/quote", + "homepage": null, + "documentation": "https://docs.rs/quote/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "regex", + "version": "1.8.1", + "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.5.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "getrandom", + "small_rng" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-cheat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-replace", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single-cheat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-utf8bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-utf8bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "crates-regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "aho-corasick": [ + "dep:aho-corasick" + ], + "default": [ + "std", + "perf", + "unicode", + "regex-syntax/default" + ], + "memchr": [ + "dep:memchr" + ], + "pattern": [], + "perf": [ + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal" + ], + "perf-cache": [], + "perf-dfa": [], + "perf-inline": [], + "perf-literal": [ + "aho-corasick", + "memchr" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment", + "regex-syntax/unicode" + ], + "unicode-age": [ + "regex-syntax/unicode-age" + ], + "unicode-bool": [ + "regex-syntax/unicode-bool" + ], + "unicode-case": [ + "regex-syntax/unicode-case" + ], + "unicode-gencat": [ + "regex-syntax/unicode-gencat" + ], + "unicode-perl": [ + "regex-syntax/unicode-perl" + ], + "unicode-script": [ + "regex-syntax/unicode-script" + ], + "unicode-segment": [ + "regex-syntax/unicode-segment" + ], + "unstable": [ + "pattern" + ], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "text-processing" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "regex-automata", + "version": "0.1.10", + "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Automata construction and matching using regular expressions.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "fst", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6.16", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.2.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.82", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_bytes", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.82", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "toml", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.10", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-automata", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/tests/tests.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "default": [ + "std" + ], + "fst": [ + "dep:fst" + ], + "regex-syntax": [ + "dep:regex-syntax" + ], + "std": [ + "regex-syntax" + ], + "transducer": [ + "std", + "fst" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "regex", + "dfa", + "automata", + "automaton", + "nfa" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/regex-automata", + "homepage": "https://github.com/BurntSushi/regex-automata", + "documentation": "https://docs.rs/regex-automata", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-syntax", + "version": "0.6.29", + "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A regular expression parser.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-syntax", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "unicode" + ], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ], + "unicode-age": [], + "unicode-bool": [], + "unicode-case": [], + "unicode-gencat": [], + "unicode-perl": [], + "unicode-script": [], + "unicode-segment": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex-syntax", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-syntax", + "version": "0.7.1", + "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A regular expression parser.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-syntax", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "std", + "unicode" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ], + "unicode-age": [], + "unicode-bool": [], + "unicode-case": [], + "unicode-gencat": [], + "unicode-perl": [], + "unicode-script": [], + "unicode-segment": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex-syntax", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "ripgrep", + "version": "13.0.0", + "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "ripgrep is a line-oriented search tool that recursively searches the current\ndirectory for a regex pattern while respecting gitignore rules. ripgrep has\nfirst class support on Windows, macOS and Linux.\n", + "source": null, + "dependencies": [ + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "clap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.33.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "suggestions" + ], + "target": null, + "registry": null + }, + { + "name": "grep", + "source": null, + "req": "^0.2.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/grep" + }, + { + "name": "ignore", + "source": null, + "req": "^0.4.19", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/ignore" + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.3.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.23", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.77", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.77", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "clap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.33.0", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "suggestions" + ], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "jemallocator", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "rg", + "src_path": "$ROOT$ripgrep/crates/core/main.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "integration", + "src_path": "$ROOT$ripgrep/tests/tests.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$ripgrep/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "pcre2": [ + "grep/pcre2" + ], + "simd-accel": [ + "grep/simd-accel" + ] + }, + "manifest_path": "$ROOT$ripgrep/Cargo.toml", + "metadata": { + "deb": { + "assets": [ + [ + "target/release/rg", + "usr/bin/", + "755" + ], + [ + "COPYING", + "usr/share/doc/ripgrep/", + "644" + ], + [ + "LICENSE-MIT", + "usr/share/doc/ripgrep/", + "644" + ], + [ + "UNLICENSE", + "usr/share/doc/ripgrep/", + "644" + ], + [ + "CHANGELOG.md", + "usr/share/doc/ripgrep/CHANGELOG", + "644" + ], + [ + "README.md", + "usr/share/doc/ripgrep/README", + "644" + ], + [ + "FAQ.md", + "usr/share/doc/ripgrep/FAQ", + "644" + ], + [ + "deployment/deb/rg.1", + "usr/share/man/man1/rg.1", + "644" + ], + [ + "deployment/deb/rg.bash", + "usr/share/bash-completion/completions/rg", + "644" + ], + [ + "deployment/deb/rg.fish", + "usr/share/fish/vendor_completions.d/rg.fish", + "644" + ], + [ + "deployment/deb/_rg", + "usr/share/zsh/vendor-completions/", + "644" + ] + ], + "extended-description": "ripgrep (rg) recursively searches your current directory for a regex pattern.\nBy default, ripgrep will respect your .gitignore and automatically skip hidden\nfiles/directories and binary files.\n", + "features": [ + "pcre2" + ], + "section": "utils" + } + }, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "command-line-utilities", + "text-processing" + ], + "keywords": [ + "regex", + "grep", + "egrep", + "search", + "pattern" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep", + "homepage": "https://github.com/BurntSushi/ripgrep", + "documentation": "https://github.com/BurntSushi/ripgrep", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.65" + }, + { + "name": "ryu", + "version": "1.0.13", + "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Apache-2.0 OR BSL-1.0", + "license_file": null, + "description": "Fast floating point to string conversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "no-panic", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "num_cpus", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand_xorshift", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "ryu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "upstream_benchmark", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/examples/upstream_benchmark.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "common_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/common_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "d2s_table_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_table_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "d2s_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "exhaustive", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/exhaustive.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "f2s_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/f2s_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "s2d_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2d_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "s2f_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2f_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "no-panic": [ + "dep:no-panic" + ], + "small": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "value-formatting", + "no-std" + ], + "keywords": [ + "float" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/ryu", + "homepage": null, + "documentation": "https://docs.rs/ryu", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.36" + }, + { + "name": "same-file", + "version": "1.0.6", + "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "A simple crate for determining whether two file paths point to the same file.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "same-file", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "is_same_file", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_same_file.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "is_stderr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_stderr.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "same", + "file", + "equal", + "inode" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/same-file", + "homepage": "https://github.com/BurntSushi/same-file", + "documentation": "https://docs.rs/same-file", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "serde", + "version": "1.0.160", + "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A generic serialization/deserialization framework", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.160", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "serde", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [], + "default": [ + "std" + ], + "derive": [ + "serde_derive" + ], + "rc": [], + "serde_derive": [ + "dep:serde_derive" + ], + "std": [], + "unstable": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "derive" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "derive", + "rc" + ] + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar ", + "David Tolnay " + ], + "categories": [ + "encoding", + "no-std" + ], + "keywords": [ + "serde", + "serialization", + "no_std" + ], + "readme": "crates-io.md", + "repository": "https://github.com/serde-rs/serde", + "homepage": "https://serde.rs", + "documentation": "https://docs.rs/serde", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": "1.19" + }, + { + "name": "serde_derive", + "version": "1.0.160", + "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "syn", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "proc-macro" + ], + "crate_types": [ + "proc-macro" + ], + "name": "serde_derive", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [], + "deserialize_in_place": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar ", + "David Tolnay " + ], + "categories": [ + "no-std" + ], + "keywords": [ + "serde", + "serialization", + "no_std", + "derive" + ], + "readme": "crates-io.md", + "repository": "https://github.com/serde-rs/serde", + "homepage": "https://serde.rs", + "documentation": "https://serde.rs/derive.html", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "serde_json", + "version": "1.0.96", + "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A JSON serialization file format", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "indexmap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.5.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "itoa", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ryu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.100", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "automod", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "indoc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ref-cast", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.100", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "serde_bytes", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_stacker", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "trybuild", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.49", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "diff" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "serde_json", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compiletest", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/compiletest.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "debug", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/debug.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "lexical", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/lexical.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "map", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/map.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "regression", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/regression.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "stream", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/stream.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [ + "serde/alloc" + ], + "arbitrary_precision": [], + "default": [ + "std" + ], + "float_roundtrip": [], + "indexmap": [ + "dep:indexmap" + ], + "preserve_order": [ + "indexmap", + "std" + ], + "raw_value": [], + "std": [ + "serde/std" + ], + "unbounded_depth": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "raw_value", + "unbounded_depth" + ], + "rustdoc-args": [ + "--cfg", + "docsrs" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "raw_value" + ] + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar ", + "David Tolnay " + ], + "categories": [ + "encoding", + "parser-implementations", + "no-std" + ], + "keywords": [ + "json", + "serde", + "serialization" + ], + "readme": "README.md", + "repository": "https://github.com/serde-rs/json", + "homepage": null, + "documentation": "https://docs.rs/serde_json", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.36" + }, + { + "name": "strsim", + "version": "0.8.0", + "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "Implementations of string similarity metrics.\nIncludes Hamming, Levenshtein, OSA, Damerau-Levenshtein, Jaro, and Jaro-Winkler.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "strsim", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "lib", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/tests/lib.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "benches", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/benches/benches.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Danny Guo " + ], + "categories": [], + "keywords": [ + "string", + "similarity", + "Hamming", + "Levenshtein", + "Jaro" + ], + "readme": "README.md", + "repository": "https://github.com/dguo/strsim-rs", + "homepage": "https://github.com/dguo/strsim-rs", + "documentation": "https://docs.rs/strsim/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "syn", + "version": "2.0.15", + "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Parser for Rust source code", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.55", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.25", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-ident", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "anyhow", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "automod", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "flate2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "insta", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rayon", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ref-cast", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "reqwest", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "blocking" + ], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "syn-test-suite", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tar", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.16", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.3.2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "syn", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "regression", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_asyncness", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_attribute", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_derive_input", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_expr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_generics", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_grouping", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_ident", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_item", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_iterators", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_lit", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_meta", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_parse_buffer", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_parse_stream", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_pat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_path", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_precedence", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_receiver", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_round_trip", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_shebang", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_should_parse", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_stmt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_token_trees", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_ty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_visibility", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "zzz_stable", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "rust", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs", + "edition": "2021", + "required-features": [ + "full", + "parsing" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "file", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs", + "edition": "2021", + "required-features": [ + "full", + "parsing" + ], + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "clone-impls": [], + "default": [ + "derive", + "parsing", + "printing", + "clone-impls", + "proc-macro" + ], + "derive": [], + "extra-traits": [], + "fold": [], + "full": [], + "parsing": [], + "printing": [ + "quote" + ], + "proc-macro": [ + "proc-macro2/proc-macro", + "quote/proc-macro" + ], + "quote": [ + "dep:quote" + ], + "test": [ + "syn-test-suite/all-features" + ], + "visit": [], + "visit-mut": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "doc_cfg" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "full", + "visit", + "visit-mut", + "fold", + "extra-traits" + ] + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "development-tools::procedural-macro-helpers", + "parser-implementations" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/syn", + "homepage": null, + "documentation": "https://docs.rs/syn", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "termcolor", + "version": "1.2.0", + "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "A simple cross platform library for writing colored text to a terminal.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "termcolor", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "windows", + "win", + "color", + "ansi", + "console" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/termcolor", + "homepage": "https://github.com/BurntSushi/termcolor", + "documentation": "https://docs.rs/termcolor", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "textwrap", + "version": "0.11.0", + "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "Textwrap is a small library for word wrapping, indenting, and\ndedenting strings.\n\nYou can use it to format strings (such as help and error messages) for\ndisplay in commandline applications. It is designed to be efficient\nand handle Unicode characters correctly.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "hyphenation", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "embed_all" + ], + "target": null, + "registry": null + }, + { + "name": "term_size", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-width", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lipsum", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand_xorshift", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "version-sync", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "textwrap", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "layout", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/layout.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "termwidth", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/termwidth.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "version-numbers", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/tests/version-numbers.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "linear", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/benches/linear.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "hyphenation": [ + "dep:hyphenation" + ], + "term_size": [ + "dep:term_size" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true + } + } + }, + "publish": null, + "authors": [ + "Martin Geisler " + ], + "categories": [ + "text-processing", + "command-line-interface" + ], + "keywords": [ + "text", + "formatting", + "wrap", + "typesetting", + "hyphenation" + ], + "readme": "README.md", + "repository": "https://github.com/mgeisler/textwrap", + "homepage": null, + "documentation": "https://docs.rs/textwrap/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "thread_local", + "version": "1.1.7", + "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Per-object thread-local storage", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "once_cell", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.5.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "thread_local", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "thread_local", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/benches/thread_local.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "nightly": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Amanieu d'Antras " + ], + "categories": [], + "keywords": [ + "thread_local", + "concurrent", + "thread" + ], + "readme": "README.md", + "repository": "https://github.com/Amanieu/thread_local-rs", + "homepage": null, + "documentation": "https://docs.rs/thread_local/", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "unicode-ident", + "version": "1.0.8", + "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016", + "license_file": null, + "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "fst", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "small_rng" + ], + "target": null, + "registry": null + }, + { + "name": "roaring", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.10", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ucd-trie", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-xid", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "unicode-ident", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compare", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "static_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "xid", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "development-tools::procedural-macro-helpers", + "no-std" + ], + "keywords": [ + "unicode", + "xid" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/unicode-ident", + "homepage": null, + "documentation": "https://docs.rs/unicode-ident", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "unicode-width", + "version": "0.1.10", + "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Determine displayed width of `char` and `str` types\naccording to Unicode Standard Annex #11 rules.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-std", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": "std", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "unicode-width", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "bench": [], + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [], + "no_std": [], + "rustc-dep-of-std": [ + "std", + "core", + "compiler_builtins" + ], + "std": [ + "dep:std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "kwantam ", + "Manish Goregaokar " + ], + "categories": [], + "keywords": [ + "text", + "width", + "unicode" + ], + "readme": "README.md", + "repository": "https://github.com/unicode-rs/unicode-width", + "homepage": "https://github.com/unicode-rs/unicode-width", + "documentation": "https://unicode-rs.github.io/unicode-width", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "walkdir", + "version": "2.3.3", + "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Recursively walk a directory.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "same-file", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "walkdir", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "filesystem" + ], + "keywords": [ + "directory", + "recursive", + "walk", + "iterator" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/walkdir", + "homepage": "https://github.com/BurntSushi/walkdir", + "documentation": "https://docs.rs/walkdir/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi", + "version": "0.3.9", + "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Raw FFI bindings for all of Windows API.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "winapi-i686-pc-windows-gnu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "i686-pc-windows-gnu", + "registry": null + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "x86_64-pc-windows-gnu", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "accctrl": [], + "aclapi": [], + "activation": [], + "adhoc": [], + "appmgmt": [], + "audioclient": [], + "audiosessiontypes": [], + "avrt": [], + "basetsd": [], + "bcrypt": [], + "bits": [], + "bits10_1": [], + "bits1_5": [], + "bits2_0": [], + "bits2_5": [], + "bits3_0": [], + "bits4_0": [], + "bits5_0": [], + "bitscfg": [], + "bitsmsg": [], + "bluetoothapis": [], + "bluetoothleapis": [], + "bthdef": [], + "bthioctl": [], + "bthledef": [], + "bthsdpdef": [], + "bugcodes": [], + "cderr": [], + "cfg": [], + "cfgmgr32": [], + "cguid": [], + "combaseapi": [], + "coml2api": [], + "commapi": [], + "commctrl": [], + "commdlg": [], + "commoncontrols": [], + "consoleapi": [], + "corecrt": [], + "corsym": [], + "d2d1": [], + "d2d1_1": [], + "d2d1_2": [], + "d2d1_3": [], + "d2d1effectauthor": [], + "d2d1effects": [], + "d2d1effects_1": [], + "d2d1effects_2": [], + "d2d1svg": [], + "d2dbasetypes": [], + "d3d": [], + "d3d10": [], + "d3d10_1": [], + "d3d10_1shader": [], + "d3d10effect": [], + "d3d10misc": [], + "d3d10sdklayers": [], + "d3d10shader": [], + "d3d11": [], + "d3d11_1": [], + "d3d11_2": [], + "d3d11_3": [], + "d3d11_4": [], + "d3d11on12": [], + "d3d11sdklayers": [], + "d3d11shader": [], + "d3d11tokenizedprogramformat": [], + "d3d12": [], + "d3d12sdklayers": [], + "d3d12shader": [], + "d3d9": [], + "d3d9caps": [], + "d3d9types": [], + "d3dcommon": [], + "d3dcompiler": [], + "d3dcsx": [], + "d3dkmdt": [], + "d3dkmthk": [], + "d3dukmdt": [], + "d3dx10core": [], + "d3dx10math": [], + "d3dx10mesh": [], + "datetimeapi": [], + "davclnt": [], + "dbghelp": [], + "dbt": [], + "dcommon": [], + "dcomp": [], + "dcompanimation": [], + "dcomptypes": [], + "dde": [], + "ddraw": [], + "ddrawi": [], + "ddrawint": [], + "debug": [ + "impl-debug" + ], + "debugapi": [], + "devguid": [], + "devicetopology": [], + "devpkey": [], + "devpropdef": [], + "dinput": [], + "dinputd": [], + "dispex": [], + "dmksctl": [], + "dmusicc": [], + "docobj": [], + "documenttarget": [], + "dot1x": [], + "dpa_dsa": [], + "dpapi": [], + "dsgetdc": [], + "dsound": [], + "dsrole": [], + "dvp": [], + "dwmapi": [], + "dwrite": [], + "dwrite_1": [], + "dwrite_2": [], + "dwrite_3": [], + "dxdiag": [], + "dxfile": [], + "dxgi": [], + "dxgi1_2": [], + "dxgi1_3": [], + "dxgi1_4": [], + "dxgi1_5": [], + "dxgi1_6": [], + "dxgidebug": [], + "dxgiformat": [], + "dxgitype": [], + "dxva2api": [], + "dxvahd": [], + "eaptypes": [], + "enclaveapi": [], + "endpointvolume": [], + "errhandlingapi": [], + "everything": [], + "evntcons": [], + "evntprov": [], + "evntrace": [], + "excpt": [], + "exdisp": [], + "fibersapi": [], + "fileapi": [], + "functiondiscoverykeys_devpkey": [], + "gl-gl": [], + "guiddef": [], + "handleapi": [], + "heapapi": [], + "hidclass": [], + "hidpi": [], + "hidsdi": [], + "hidusage": [], + "highlevelmonitorconfigurationapi": [], + "hstring": [], + "http": [], + "ifdef": [], + "ifmib": [], + "imm": [], + "impl-debug": [], + "impl-default": [], + "in6addr": [], + "inaddr": [], + "inspectable": [], + "interlockedapi": [], + "intsafe": [], + "ioapiset": [], + "ipexport": [], + "iphlpapi": [], + "ipifcons": [], + "ipmib": [], + "iprtrmib": [], + "iptypes": [], + "jobapi": [], + "jobapi2": [], + "knownfolders": [], + "ks": [], + "ksmedia": [], + "ktmtypes": [], + "ktmw32": [], + "l2cmn": [], + "libloaderapi": [], + "limits": [], + "lmaccess": [], + "lmalert": [], + "lmapibuf": [], + "lmat": [], + "lmcons": [], + "lmdfs": [], + "lmerrlog": [], + "lmjoin": [], + "lmmsg": [], + "lmremutl": [], + "lmrepl": [], + "lmserver": [], + "lmshare": [], + "lmstats": [], + "lmsvc": [], + "lmuse": [], + "lmwksta": [], + "lowlevelmonitorconfigurationapi": [], + "lsalookup": [], + "memoryapi": [], + "minschannel": [], + "minwinbase": [], + "minwindef": [], + "mmdeviceapi": [], + "mmeapi": [], + "mmreg": [], + "mmsystem": [], + "mprapidef": [], + "msaatext": [], + "mscat": [], + "mschapp": [], + "mssip": [], + "mstcpip": [], + "mswsock": [], + "mswsockdef": [], + "namedpipeapi": [], + "namespaceapi": [], + "nb30": [], + "ncrypt": [], + "netioapi": [], + "nldef": [], + "ntddndis": [], + "ntddscsi": [], + "ntddser": [], + "ntdef": [], + "ntlsa": [], + "ntsecapi": [], + "ntstatus": [], + "oaidl": [], + "objbase": [], + "objidl": [], + "objidlbase": [], + "ocidl": [], + "ole2": [], + "oleauto": [], + "olectl": [], + "oleidl": [], + "opmapi": [], + "pdh": [], + "perflib": [], + "physicalmonitorenumerationapi": [], + "playsoundapi": [], + "portabledevice": [], + "portabledeviceapi": [], + "portabledevicetypes": [], + "powerbase": [], + "powersetting": [], + "powrprof": [], + "processenv": [], + "processsnapshot": [], + "processthreadsapi": [], + "processtopologyapi": [], + "profileapi": [], + "propidl": [], + "propkey": [], + "propkeydef": [], + "propsys": [], + "prsht": [], + "psapi": [], + "qos": [], + "realtimeapiset": [], + "reason": [], + "restartmanager": [], + "restrictederrorinfo": [], + "rmxfguid": [], + "roapi": [], + "robuffer": [], + "roerrorapi": [], + "rpc": [], + "rpcdce": [], + "rpcndr": [], + "rtinfo": [], + "sapi": [], + "sapi51": [], + "sapi53": [], + "sapiddk": [], + "sapiddk51": [], + "schannel": [], + "sddl": [], + "securityappcontainer": [], + "securitybaseapi": [], + "servprov": [], + "setupapi": [], + "shellapi": [], + "shellscalingapi": [], + "shlobj": [], + "shobjidl": [], + "shobjidl_core": [], + "shtypes": [], + "softpub": [], + "spapidef": [], + "spellcheck": [], + "sporder": [], + "sql": [], + "sqlext": [], + "sqltypes": [], + "sqlucode": [], + "sspi": [], + "std": [], + "stralign": [], + "stringapiset": [], + "strmif": [], + "subauth": [], + "synchapi": [], + "sysinfoapi": [], + "systemtopologyapi": [], + "taskschd": [], + "tcpestats": [], + "tcpmib": [], + "textstor": [], + "threadpoolapiset": [], + "threadpoollegacyapiset": [], + "timeapi": [], + "timezoneapi": [], + "tlhelp32": [], + "transportsettingcommon": [], + "tvout": [], + "udpmib": [], + "unknwnbase": [], + "urlhist": [], + "urlmon": [], + "usb": [], + "usbioctl": [], + "usbiodef": [], + "usbscan": [], + "usbspec": [], + "userenv": [], + "usp10": [], + "utilapiset": [], + "uxtheme": [], + "vadefs": [], + "vcruntime": [], + "vsbackup": [], + "vss": [], + "vsserror": [], + "vswriter": [], + "wbemads": [], + "wbemcli": [], + "wbemdisp": [], + "wbemprov": [], + "wbemtran": [], + "wct": [], + "werapi": [], + "winbase": [], + "wincodec": [], + "wincodecsdk": [], + "wincon": [], + "wincontypes": [], + "wincred": [], + "wincrypt": [], + "windef": [], + "windot11": [], + "windowsceip": [], + "windowsx": [], + "winefs": [], + "winerror": [], + "winevt": [], + "wingdi": [], + "winhttp": [], + "wininet": [], + "winineti": [], + "winioctl": [], + "winnetwk": [], + "winnls": [], + "winnt": [], + "winreg": [], + "winsafer": [], + "winscard": [], + "winsmcrd": [], + "winsock2": [], + "winspool": [], + "winstring": [], + "winsvc": [], + "wintrust": [], + "winusb": [], + "winusbio": [], + "winuser": [], + "winver": [], + "wlanapi": [], + "wlanihv": [], + "wlanihvtypes": [], + "wlantypes": [], + "wlclient": [], + "wmistr": [], + "wnnc": [], + "wow64apiset": [], + "wpdmtpextensions": [], + "ws2bth": [], + "ws2def": [], + "ws2ipdef": [], + "ws2spi": [], + "ws2tcpip": [], + "wtsapi32": [], + "wtypes": [], + "wtypesbase": [], + "xinput": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "default-target": "x86_64-pc-windows-msvc", + "features": [ + "everything", + "impl-debug", + "impl-default" + ], + "targets": [ + "aarch64-pc-windows-msvc", + "i686-pc-windows-msvc", + "x86_64-pc-windows-msvc" + ] + } + } + }, + "publish": null, + "authors": [ + "Peter Atashian " + ], + "categories": [ + "external-ffi-bindings", + "no-std", + "os::windows-apis" + ], + "keywords": [ + "windows", + "ffi", + "win32", + "com", + "directx" + ], + "readme": "README.md", + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": "https://docs.rs/winapi/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-i686-pc-windows-gnu", + "version": "0.4.0", + "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-i686-pc-windows-gnu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Peter Atashian " + ], + "categories": [], + "keywords": [ + "windows" + ], + "readme": null, + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-util", + "version": "0.1.5", + "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "A dumping ground for high level safe wrappers over winapi.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "winapi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "std", + "consoleapi", + "errhandlingapi", + "fileapi", + "minwindef", + "processenv", + "winbase", + "wincon", + "winerror", + "winnt" + ], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-util", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-pc-windows-msvc" + ] + } + } + }, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "os::windows-apis", + "external-ffi-bindings" + ], + "keywords": [ + "windows", + "winapi", + "util", + "win" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/winapi-util", + "homepage": "https://github.com/BurntSushi/winapi-util", + "documentation": "https://docs.rs/winapi-util", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "version": "0.4.0", + "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-x86_64-pc-windows-gnu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Peter Atashian " + ], + "categories": [], + "keywords": [ + "windows" + ], + "readme": null, + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + } + ], + "workspace_members": [ + "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", + "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)" + ], + "resolve": { + "nodes": [ + { + "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "std" + ] + }, + { + "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "perf-literal", + "std" + ] + }, + { + "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "hermit_abi", + "pkg": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(target_os = \"hermit\")" + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + }, + { + "name": "winapi", + "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default" + ] + }, + { + "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "once_cell", + "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_automata", + "pkg": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "alloc", + "default", + "std", + "unicode" + ] + }, + { + "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "runtime-dispatch-simd" + ] + }, + { + "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "jobserver", + "pkg": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "jobserver", + "parallel" + ] + }, + { + "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "bitflags", + "pkg": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "strsim", + "pkg": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "textwrap", + "pkg": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "unicode_width", + "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "strsim", + "suggestions" + ] + }, + { + "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "crossbeam_utils", + "pkg": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "crossbeam-utils", + "default", + "std" + ] + }, + { + "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "std" + ] + }, + { + "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "alloc", + "default" + ] + }, + { + "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "encoding_rs", + "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "dependencies": [ + "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "aho_corasick", + "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "fnv", + "pkg": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "glob", + "pkg": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde_json", + "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [ + "default", + "log" + ] + }, + { + "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "dependencies": [ + "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "grep_cli", + "pkg": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_printer", + "pkg": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_regex", + "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_searcher", + "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "termcolor", + "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "walkdir", + "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "dependencies": [ + "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "atty", + "pkg": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "globset", + "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "same_file", + "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "termcolor", + "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", + "dependencies": [ + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "pcre2", + "pkg": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "dependencies": [ + "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "base64", + "pkg": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_regex", + "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "grep_searcher", + "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde_json", + "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "termcolor", + "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "base64", + "default", + "serde", + "serde1", + "serde_json" + ] + }, + { + "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "dependencies": [ + "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "aho_corasick", + "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "thread_local", + "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "dependencies": [ + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bytecount", + "pkg": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "encoding_rs", + "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "encoding_rs_io", + "pkg": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_regex", + "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "memmap", + "pkg": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [ + "default" + ] + }, + { + "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default" + ] + }, + { + "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "dependencies": [ + "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "crossbeam_channel", + "pkg": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "globset", + "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "same_file", + "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "thread_local", + "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "walkdir", + "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cc", + "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "background_threads_runtime_support" + ] + }, + { + "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "jemalloc_sys", + "pkg": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "background_threads_runtime_support", + "default" + ] + }, + { + "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + } + ], + "features": [] + }, + { + "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + } + ], + "features": [] + }, + { + "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "alloc", + "default", + "race", + "std" + ] + }, + { + "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "pcre2_sys", + "pkg": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "thread_local", + "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cc", + "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "pkg_config", + "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "unicode_ident", + "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "proc-macro" + ] + }, + { + "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "proc-macro" + ] + }, + { + "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "aho_corasick", + "pkg": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "aho-corasick", + "default", + "memchr", + "perf", + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal", + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)", + "dependencies": [ + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", + "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "clap", + "pkg": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + }, + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "grep", + "pkg": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "ignore", + "pkg": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "jemallocator", + "pkg": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))" + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + }, + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "serde_derive", + "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "serde_json", + "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "termcolor", + "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "walkdir", + "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "serde_derive", + "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "alloc", + "default", + "derive", + "serde_derive", + "std" + ] + }, + { + "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quote", + "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "syn", + "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default" + ] + }, + { + "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "itoa", + "pkg": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "ryu", + "pkg": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "std" + ] + }, + { + "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quote", + "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "unicode_ident", + "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "clone-impls", + "default", + "derive", + "parsing", + "printing", + "proc-macro", + "quote" + ] + }, + { + "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "unicode_width", + "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "once_cell", + "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default" + ] + }, + { + "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "same_file", + "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi_i686_pc_windows_gnu", + "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "i686-pc-windows-gnu" + } + ] + }, + { + "name": "winapi_x86_64_pc_windows_gnu", + "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "x86_64-pc-windows-gnu" + } + ] + } + ], + "features": [ + "consoleapi", + "errhandlingapi", + "fileapi", + "minwinbase", + "minwindef", + "processenv", + "std", + "winbase", + "wincon", + "winerror", + "winnt" + ] + }, + { + "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi", + "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + } + ], + "root": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)" + }, + "target_directory": "$ROOT$ripgrep/target", + "version": 1, + "workspace_root": "$ROOT$ripgrep", + "metadata": null +} diff --git a/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs index 88db6093ee0e..d761a5e798e8 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs @@ -242,7 +242,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream let tracing = if let QueryStorage::Memoized | QueryStorage::LruMemoized = query.storage { let s = format!("{trait_name}::{fn_name}"); Some(quote! { - let _p = tracing::debug_span!(#s, #(#key_names = tracing::field::debug(&#key_names)),*).entered(); + let _p = tracing::trace_span!(#s, #(#key_names = tracing::field::debug(&#key_names)),*).entered(); }) } else { None diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs index 6c5ccba173b9..cfe2c48f411f 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs @@ -13,7 +13,7 @@ use crate::{Database, DatabaseKeyIndex, Event, EventKind, QueryDb}; use parking_lot::{RawRwLock, RwLock}; use std::ops::Deref; use std::sync::atomic::{AtomicBool, Ordering}; -use tracing::{debug, info}; +use tracing::trace; pub(super) struct Slot where @@ -126,7 +126,7 @@ where // doing any `set` invocations while the query function runs. let revision_now = runtime.current_revision(); - info!("{:?}: invoked at {:?}", self, revision_now,); + trace!("{:?}: invoked at {:?}", self, revision_now,); // First, do a check with a read-lock. loop { @@ -152,7 +152,7 @@ where ) -> StampedValue { let runtime = db.salsa_runtime(); - debug!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,); + trace!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,); // Check with an upgradable read to see if there is a value // already. (This permits other readers but prevents anyone @@ -184,7 +184,7 @@ where // inputs and check whether they are out of date. if let Some(memo) = &mut old_memo { if let Some(value) = memo.verify_value(db.ops_database(), revision_now, &active_query) { - info!("{:?}: validated old memoized value", self,); + trace!("{:?}: validated old memoized value", self,); db.salsa_event(Event { runtime_id: runtime.id(), @@ -212,7 +212,7 @@ where old_memo: Option>, key: &Q::Key, ) -> StampedValue { - tracing::info!("{:?}: executing query", self.database_key_index().debug(db)); + tracing::trace!("{:?}: executing query", self.database_key_index().debug(db)); db.salsa_event(Event { runtime_id: db.salsa_runtime().id(), @@ -224,7 +224,7 @@ where let value = match Cycle::catch(|| Q::execute(db, key.clone())) { Ok(v) => v, Err(cycle) => { - tracing::debug!( + tracing::trace!( "{:?}: caught cycle {:?}, have strategy {:?}", self.database_key_index().debug(db), cycle, @@ -272,9 +272,10 @@ where // consumers must be aware of. Becoming *more* durable // is not. See the test `constant_to_non_constant`. if revisions.durability >= old_memo.revisions.durability && old_memo.value == value { - debug!( + trace!( "read_upgrade({:?}): value is equal, back-dating to {:?}", - self, old_memo.revisions.changed_at, + self, + old_memo.revisions.changed_at, ); assert!(old_memo.revisions.changed_at <= revisions.changed_at); @@ -290,7 +291,7 @@ where let memo_value = new_value.value.clone(); - debug!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,); + trace!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,); panic_guard.proceed(Some(Memo { value: memo_value, verified_at: revision_now, revisions })); @@ -339,9 +340,11 @@ where } QueryState::Memoized(memo) => { - debug!( + trace!( "{:?}: found memoized value, verified_at={:?}, changed_at={:?}", - self, memo.verified_at, memo.revisions.changed_at, + self, + memo.verified_at, + memo.revisions.changed_at, ); if memo.verified_at < revision_now { @@ -355,7 +358,7 @@ where value: value.clone(), }; - info!("{:?}: returning memoized value changed at {:?}", self, value.changed_at); + trace!("{:?}: returning memoized value changed at {:?}", self, value.changed_at); ProbeState::UpToDate(value) } @@ -387,7 +390,7 @@ where } pub(super) fn invalidate(&self, new_revision: Revision) -> Option { - tracing::debug!("Slot::invalidate(new_revision = {:?})", new_revision); + tracing::trace!("Slot::invalidate(new_revision = {:?})", new_revision); match &mut *self.state.write() { QueryState::Memoized(memo) => { memo.revisions.untracked = true; @@ -411,9 +414,11 @@ where db.unwind_if_cancelled(); - debug!( + trace!( "maybe_changed_after({:?}) called with revision={:?}, revision_now={:?}", - self, revision, revision_now, + self, + revision, + revision_now, ); // Do an initial probe with just the read-lock. @@ -680,9 +685,11 @@ where assert!(self.verified_at != revision_now); let verified_at = self.verified_at; - debug!( + trace!( "verify_revisions: verified_at={:?}, revision_now={:?}, inputs={:#?}", - verified_at, revision_now, self.revisions.inputs + verified_at, + revision_now, + self.revisions.inputs ); if self.check_durability(db.salsa_runtime()) { @@ -708,7 +715,7 @@ where let changed_input = inputs.slice.iter().find(|&&input| db.maybe_changed_after(input, verified_at)); if let Some(input) = changed_input { - debug!("validate_memoized_value: `{:?}` may have changed", input); + trace!("validate_memoized_value: `{:?}` may have changed", input); return false; } @@ -721,7 +728,7 @@ where /// True if this memo is known not to have changed based on its durability. fn check_durability(&self, runtime: &Runtime) -> bool { let last_changed = runtime.last_changed_revision(self.revisions.durability); - debug!( + trace!( "check_durability(last_changed={:?} <= verified_at={:?}) = {:?}", last_changed, self.verified_at, diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs index ff9cc4eade2c..73a5e07aa05a 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs @@ -17,7 +17,7 @@ use parking_lot::{RawRwLock, RwLock}; use std::marker::PhantomData; use std::ops::Deref; use std::sync::atomic::{AtomicBool, Ordering}; -use tracing::{debug, info}; +use tracing::trace; pub(super) struct Slot where @@ -140,7 +140,7 @@ where // doing any `set` invocations while the query function runs. let revision_now = runtime.current_revision(); - info!("{:?}: invoked at {:?}", self, revision_now,); + trace!("{:?}: invoked at {:?}", self, revision_now,); // First, do a check with a read-lock. loop { @@ -168,7 +168,7 @@ where ) -> StampedValue { let runtime = db.salsa_runtime(); - debug!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,); + trace!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,); // Check with an upgradable read to see if there is a value // already. (This permits other readers but prevents anyone @@ -202,7 +202,7 @@ where // inputs and check whether they are out of date. if let Some(memo) = &mut old_memo { if let Some(value) = memo.verify_value(db.ops_database(), revision_now, &active_query) { - info!("{:?}: validated old memoized value", self,); + trace!("{:?}: validated old memoized value", self,); db.salsa_event(Event { runtime_id: runtime.id(), @@ -230,7 +230,7 @@ where old_memo: Option>, key: &Q::Key, ) -> StampedValue { - tracing::info!("{:?}: executing query", self.database_key_index().debug(db)); + tracing::trace!("{:?}: executing query", self.database_key_index().debug(db)); db.salsa_event(Event { runtime_id: db.salsa_runtime().id(), @@ -242,7 +242,7 @@ where let value = match Cycle::catch(|| Q::execute(db, key.clone())) { Ok(v) => v, Err(cycle) => { - tracing::debug!( + tracing::trace!( "{:?}: caught cycle {:?}, have strategy {:?}", self.database_key_index().debug(db), cycle, @@ -293,9 +293,10 @@ where if revisions.durability >= old_memo.revisions.durability && MP::memoized_value_eq(old_value, &value) { - debug!( + trace!( "read_upgrade({:?}): value is equal, back-dating to {:?}", - self, old_memo.revisions.changed_at, + self, + old_memo.revisions.changed_at, ); assert!(old_memo.revisions.changed_at <= revisions.changed_at); @@ -313,7 +314,7 @@ where let memo_value = if self.should_memoize_value(key) { Some(new_value.value.clone()) } else { None }; - debug!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,); + trace!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,); panic_guard.proceed(Some(Memo { value: memo_value, verified_at: revision_now, revisions })); @@ -362,9 +363,11 @@ where } QueryState::Memoized(memo) => { - debug!( + trace!( "{:?}: found memoized value, verified_at={:?}, changed_at={:?}", - self, memo.verified_at, memo.revisions.changed_at, + self, + memo.verified_at, + memo.revisions.changed_at, ); if memo.verified_at < revision_now { @@ -378,7 +381,11 @@ where value: value.clone(), }; - info!("{:?}: returning memoized value changed at {:?}", self, value.changed_at); + trace!( + "{:?}: returning memoized value changed at {:?}", + self, + value.changed_at + ); ProbeState::UpToDate(value) } else { @@ -426,7 +433,7 @@ where } pub(super) fn invalidate(&self, new_revision: Revision) -> Option { - tracing::debug!("Slot::invalidate(new_revision = {:?})", new_revision); + tracing::trace!("Slot::invalidate(new_revision = {:?})", new_revision); match &mut *self.state.write() { QueryState::Memoized(memo) => { memo.revisions.untracked = true; @@ -450,9 +457,11 @@ where db.unwind_if_cancelled(); - debug!( + trace!( "maybe_changed_after({:?}) called with revision={:?}, revision_now={:?}", - self, revision, revision_now, + self, + revision, + revision_now, ); // Do an initial probe with just the read-lock. @@ -734,9 +743,11 @@ where assert!(self.verified_at != revision_now); let verified_at = self.verified_at; - debug!( + trace!( "verify_revisions: verified_at={:?}, revision_now={:?}, inputs={:#?}", - verified_at, revision_now, self.revisions.inputs + verified_at, + revision_now, + self.revisions.inputs ); if self.check_durability(db.salsa_runtime()) { @@ -762,7 +773,7 @@ where let changed_input = inputs.slice.iter().find(|&&input| db.maybe_changed_after(input, verified_at)); if let Some(input) = changed_input { - debug!("validate_memoized_value: `{:?}` may have changed", input); + trace!("validate_memoized_value: `{:?}` may have changed", input); return false; } @@ -775,7 +786,7 @@ where /// True if this memo is known not to have changed based on its durability. fn check_durability(&self, runtime: &Runtime) -> bool { let last_changed = runtime.last_changed_revision(self.revisions.durability); - debug!( + trace!( "check_durability(last_changed={:?} <= verified_at={:?}) = {:?}", last_changed, self.verified_at, diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs index f04f48e3bab8..4992a0c7271c 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs @@ -14,7 +14,7 @@ use crate::{DatabaseKeyIndex, QueryDb}; use indexmap::map::Entry; use parking_lot::RwLock; use std::iter; -use tracing::debug; +use tracing::trace; /// Input queries store the result plus a list of the other queries /// that they invoked. This means we can avoid recomputing them when @@ -73,11 +73,11 @@ where return true; }; - debug!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,); + trace!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,); let changed_at = slot.stamped_value.read().changed_at; - debug!("maybe_changed_after: changed_at = {:?}", changed_at); + trace!("maybe_changed_after: changed_at = {:?}", changed_at); changed_at > revision } @@ -140,7 +140,7 @@ where Q: Query, { fn set(&self, runtime: &mut Runtime, key: &Q::Key, value: Q::Value, durability: Durability) { - tracing::debug!("{:?}({:?}) = {:?} ({:?})", Q::default(), key, value, durability); + tracing::trace!("{:?}({:?}) = {:?} ({:?})", Q::default(), key, value, durability); // The value is changing, so we need a new revision (*). We also // need to update the 'last changed' revision by invoking @@ -234,14 +234,14 @@ where ) -> bool { debug_assert!(revision < db.salsa_runtime().current_revision()); - debug!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,); + trace!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,); let Some(value) = &*self.slot.stamped_value.read() else { return true; }; let changed_at = value.changed_at; - debug!("maybe_changed_after: changed_at = {:?}", changed_at); + trace!("maybe_changed_after: changed_at = {:?}", changed_at); changed_at > revision } @@ -298,7 +298,7 @@ where Q: Query, { fn set(&self, runtime: &mut Runtime, (): &Q::Key, value: Q::Value, durability: Durability) { - tracing::debug!("{:?} = {:?} ({:?})", Q::default(), value, durability); + tracing::trace!("{:?} = {:?} ({:?})", Q::default(), value, durability); // The value is changing, so we need a new revision (*). We also // need to update the 'last changed' revision by invoking diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs index 8530521d9157..843b6d31f0c3 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs @@ -79,7 +79,7 @@ pub trait Database: plumbing::DatabaseOps { let current_revision = runtime.current_revision(); let pending_revision = runtime.pending_revision(); - tracing::debug!( + tracing::trace!( "unwind_if_cancelled: current_revision={:?}, pending_revision={:?}", current_revision, pending_revision @@ -684,7 +684,7 @@ impl Cycle { } pub(crate) fn throw(self) -> ! { - tracing::debug!("throwing cycle {:?}", self); + tracing::trace!("throwing cycle {:?}", self); std::panic::resume_unwind(Box::new(self)) } diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs index a6f96beeab11..7fbd42f92627 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs @@ -103,11 +103,11 @@ where /// Records that `node` was used. This may displace an old node (if the LRU limits are pub(crate) fn record_use(&self, node: &Arc) -> Option> { - tracing::debug!("record_use(node={:?})", node); + tracing::trace!("record_use(node={:?})", node); // Load green zone length and check if the LRU cache is even enabled. let green_zone = self.green_zone.load(Ordering::Acquire); - tracing::debug!("record_use: green_zone={}", green_zone); + tracing::trace!("record_use: green_zone={}", green_zone); if green_zone == 0 { return None; } @@ -115,7 +115,7 @@ where // Find current index of list (if any) and the current length // of our green zone. let index = node.lru_index().load(); - tracing::debug!("record_use: index={}", index); + tracing::trace!("record_use: index={}", index); // Already a member of the list, and in the green zone -- nothing to do! if index < green_zone { @@ -162,9 +162,9 @@ where let entries = std::mem::replace(&mut self.entries, Vec::with_capacity(self.end_red_zone as usize)); - tracing::debug!("green_zone = {:?}", self.green_zone()); - tracing::debug!("yellow_zone = {:?}", self.yellow_zone()); - tracing::debug!("red_zone = {:?}", self.red_zone()); + tracing::trace!("green_zone = {:?}", self.green_zone()); + tracing::trace!("yellow_zone = {:?}", self.yellow_zone()); + tracing::trace!("red_zone = {:?}", self.red_zone()); // We expect to resize when the LRU cache is basically empty. // So just forget all the old LRU indices to start. @@ -180,7 +180,7 @@ where /// list may displace an old member of the red zone, in which case /// that is returned. fn record_use(&mut self, node: &Arc) -> Option> { - tracing::debug!("record_use(node={:?})", node); + tracing::trace!("record_use(node={:?})", node); // NB: When this is invoked, we have typically already loaded // the LRU index (to check if it is in green zone). But that @@ -212,7 +212,7 @@ where if len < self.end_red_zone { self.entries.push(node.clone()); node.lru_index().store(len); - tracing::debug!("inserted node {:?} at {}", node, len); + tracing::trace!("inserted node {:?} at {}", node, len); return self.record_use(node); } @@ -220,7 +220,7 @@ where // zone and then promoting. let victim_index = self.pick_index(self.red_zone()); let victim_node = std::mem::replace(&mut self.entries[victim_index as usize], node.clone()); - tracing::debug!("evicting red node {:?} from {}", victim_node, victim_index); + tracing::trace!("evicting red node {:?} from {}", victim_node, victim_index); victim_node.lru_index().clear(); self.promote_red_to_green(node, victim_index); Some(victim_node) @@ -241,7 +241,7 @@ where // going to invoke `self.promote_yellow` next, and it will get // updated then. let yellow_index = self.pick_index(self.yellow_zone()); - tracing::debug!( + tracing::trace!( "demoting yellow node {:?} from {} to red at {}", self.entries[yellow_index as usize], yellow_index, @@ -265,7 +265,7 @@ where // Pick a yellow at random and switch places with it. let green_index = self.pick_index(self.green_zone()); - tracing::debug!( + tracing::trace!( "demoting green node {:?} from {} to yellow at {}", self.entries[green_index as usize], green_index, @@ -275,7 +275,7 @@ where self.entries[yellow_index as usize].lru_index().store(yellow_index); node.lru_index().store(green_index); - tracing::debug!("promoted {:?} to green index {}", node, green_index); + tracing::trace!("promoted {:?} to green index {}", node, green_index); } fn pick_index(&mut self, zone: std::ops::Range) -> u16 { diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs index 5fe5f4b46d3e..cb16ba0044df 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs @@ -9,7 +9,7 @@ use parking_lot::{Mutex, RwLock}; use std::hash::Hash; use std::panic::panic_any; use std::sync::atomic::{AtomicU32, Ordering}; -use tracing::debug; +use tracing::trace; use triomphe::{Arc, ThinArc}; mod dependency_graph; @@ -177,7 +177,7 @@ impl Runtime { where F: FnOnce(Revision) -> Option, { - tracing::debug!("increment_revision()"); + tracing::trace!("increment_revision()"); if !self.permits_increment() { panic!("increment_revision invoked during a query computation"); @@ -196,7 +196,7 @@ impl Runtime { let new_revision = current_revision.next(); - debug!("increment_revision: incremented to {:?}", new_revision); + trace!("increment_revision: incremented to {:?}", new_revision); if let Some(d) = op(new_revision) { for rev in &self.shared_state.revisions[1..=d.index()] { @@ -267,7 +267,7 @@ impl Runtime { database_key_index: DatabaseKeyIndex, to_id: RuntimeId, ) { - debug!("unblock_cycle_and_maybe_throw(database_key={:?})", database_key_index); + trace!("unblock_cycle_and_maybe_throw(database_key={:?})", database_key_index); let mut from_stack = self.local_state.take_query_stack(); let from_id = self.id(); @@ -305,7 +305,7 @@ impl Runtime { Cycle::new(Arc::new(v)) }; - debug!("cycle {:?}, cycle_query {:#?}", cycle.debug(db), cycle_query,); + trace!("cycle {:?}, cycle_query {:#?}", cycle.debug(db), cycle_query,); // We can remove the cycle participants from the list of dependencies; // they are a strongly connected component (SCC) and we only care about @@ -323,7 +323,7 @@ impl Runtime { CycleRecoveryStrategy::Fallback => false, }) .for_each(|aq| { - debug!("marking {:?} for fallback", aq.database_key_index.debug(db)); + trace!("marking {:?} for fallback", aq.database_key_index.debug(db)); aq.take_inputs_from(&cycle_query); assert!(aq.cycle.is_none()); aq.cycle = Some(cycle.clone()); diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs index 738696718868..4ab4bad0cc50 100644 --- a/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs @@ -1,4 +1,4 @@ -use tracing::debug; +use tracing::trace; use triomphe::ThinArc; use crate::durability::Durability; @@ -78,7 +78,7 @@ impl LocalState { durability: Durability, changed_at: Revision, ) { - debug!( + trace!( "report_query_read_and_unwind_if_cycle_resulted(input={:?}, durability={:?}, changed_at={:?})", input, durability, changed_at ); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index fa9ff6b56df0..d06130ce8c59 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -76,7 +76,7 @@ vfs.workspace = true paths.workspace = true [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.52", features = ["Win32_System_Threading"] } +windows-sys = { version = "0.52", features = ["Win32_System_Diagnostics_Debug", "Win32_System_Threading"] } [target.'cfg(not(target_env = "msvc"))'.dependencies] jemallocator = { version = "0.5.0", package = "tikv-jemallocator", optional = true } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/build.rs b/src/tools/rust-analyzer/crates/rust-analyzer/build.rs index 72b741de00ee..0fd381d61221 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/build.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/build.rs @@ -32,6 +32,7 @@ fn set_rerun() { } fn set_commit_info() { + #[allow(clippy::disallowed_methods)] let output = match Command::new("git") .arg("log") .arg("-1") diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index a753621eca8e..1a9cdef256d2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -44,11 +44,7 @@ fn actual_main() -> anyhow::Result { #[cfg(debug_assertions)] if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() { - #[allow(unused_mut)] - let mut d = 4; - while d == 4 { - d = 4; - } + wait_for_debugger(); } if let Err(e) = setup_logging(flags.log_file.clone()) { @@ -96,6 +92,28 @@ fn actual_main() -> anyhow::Result { Ok(ExitCode::SUCCESS) } +#[cfg(debug_assertions)] +fn wait_for_debugger() { + #[cfg(target_os = "windows")] + { + use windows_sys::Win32::System::Diagnostics::Debug::IsDebuggerPresent; + // SAFETY: WinAPI generated code that is defensively marked `unsafe` but + // in practice can not be used in an unsafe way. + while unsafe { IsDebuggerPresent() } == 0 { + std::thread::sleep(std::time::Duration::from_millis(100)); + } + } + #[cfg(not(target_os = "windows"))] + { + #[allow(unused_mut)] + let mut d = 4; + while d == 4 { + d = 4; + std::thread::sleep(std::time::Duration::from_millis(100)); + } + } +} + fn setup_logging(log_file_flag: Option) -> anyhow::Result<()> { if cfg!(windows) { // This is required so that windows finds our pdb that is placed right beside the exe. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/rustc_wrapper.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/rustc_wrapper.rs index 684b3f52afc8..b9fcd2e18709 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/rustc_wrapper.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/rustc_wrapper.rs @@ -46,6 +46,7 @@ fn run_rustc_skipping_cargo_checking( } fn run_rustc(rustc_executable: OsString, args: Vec) -> io::Result { + #[allow(clippy::disallowed_methods)] let mut child = Command::new(rustc_executable) .args(args) .stdin(Stdio::inherit()) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 66cd2e424e38..afe3455b7805 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -66,10 +66,6 @@ impl flags::AnalysisStats { true => None, false => Some(RustLibSource::Discover), }, - sysroot_query_metadata: match self.no_query_sysroot_metadata { - true => project_model::SysrootQueryMetadata::None, - false => project_model::SysrootQueryMetadata::CargoMetadata, - }, all_targets: true, set_test: !self.no_test, cfg_overrides: CfgOverrides { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index 920a2a37efb6..ff24602144a9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -71,9 +71,6 @@ xflags::xflags! { optional --with-deps /// Don't load sysroot crates (`std`, `core` & friends). optional --no-sysroot - /// Don't run cargo metadata on the sysroot to analyze its third-party dependencies. - /// Requires --no-sysroot to not be set. - optional --no-query-sysroot-metadata /// Don't set #[cfg(test)]. optional --no-test @@ -238,7 +235,6 @@ pub struct AnalysisStats { pub only: Option, pub with_deps: bool, pub no_sysroot: bool, - pub no_query_sysroot_metadata: bool, pub no_test: bool, pub disable_build_scripts: bool, pub disable_proc_macros: bool, 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 33c4f31fbee4..eb5c44418b72 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 @@ -4,8 +4,9 @@ use std::env; use std::time::Instant; use ide::{ - Analysis, AnalysisHost, FileId, FileRange, MonikerKind, PackageInformation, RootDatabase, - StaticIndex, StaticIndexedFile, TokenId, TokenStaticData, VendoredLibrariesConfig, + Analysis, AnalysisHost, FileId, FileRange, MonikerKind, MonikerResult, PackageInformation, + RootDatabase, StaticIndex, StaticIndexedFile, TokenId, TokenStaticData, + VendoredLibrariesConfig, }; use ide_db::{line_index::WideEncoding, LineIndexDatabase}; use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice}; @@ -167,7 +168,7 @@ impl LsifManager<'_, '_> { out_v: result_set_id.into(), })); } - if let Some(moniker) = token.moniker { + if let Some(MonikerResult::Moniker(moniker)) = token.moniker { let package_id = self.get_package_id(moniker.package_information); let moniker_id = self.add_vertex(lsif::Vertex::Moniker(lsp_types::Moniker { scheme: "rust-analyzer".to_owned(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 6483afc85b21..6b0ce4db7c93 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -10,10 +10,10 @@ use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig}; use itertools::Either; use paths::Utf8PathBuf; use profile::StopWatch; -use project_model::target_data_layout::RustcDataLayoutConfig; +use project_model::toolchain_info::{target_data_layout, QueryConfig}; use project_model::{ - target_data_layout, CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, - RustLibSource, Sysroot, SysrootQueryMetadata, + CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource, Sysroot, + SysrootSourceWorkspaceConfig, }; use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice}; @@ -74,13 +74,10 @@ impl Tester { ..Default::default() }; - let sysroot = Sysroot::discover( - tmp_file.parent().unwrap(), - &cargo_config.extra_env, - SysrootQueryMetadata::CargoMetadata, - ); + let mut sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env); + sysroot.load_workspace(&SysrootSourceWorkspaceConfig::default_cargo()); let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Rustc(&sysroot), + QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()), None, &cargo_config.extra_env, ); @@ -89,7 +86,6 @@ impl Tester { kind: ProjectWorkspaceKind::DetachedFile { file: ManifestPath::try_from(tmp_file).unwrap(), cargo: None, - cargo_config_extra_env: Default::default(), set_test: true, }, sysroot, @@ -302,7 +298,7 @@ impl flags::RustcTests { continue; } } - if p.extension().map_or(true, |x| x != "rs") { + if p.extension().is_none_or(|x| x != "rs") { continue; } if let Err(e) = std::panic::catch_unwind({ 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 ff009e69547a..6ca7d9ac057e 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 @@ -3,14 +3,16 @@ use std::{path::PathBuf, time::Instant}; use ide::{ - AnalysisHost, LineCol, MonikerDescriptorKind, MonikerResult, StaticIndex, StaticIndexedFile, - SymbolInformationKind, TextRange, TokenId, VendoredLibrariesConfig, + AnalysisHost, LineCol, Moniker, MonikerDescriptorKind, MonikerIdentifier, MonikerResult, + RootDatabase, StaticIndex, StaticIndexedFile, SymbolInformationKind, TextRange, TokenId, + TokenStaticData, VendoredLibrariesConfig, }; use ide_db::LineIndexDatabase; use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; use rustc_hash::{FxHashMap, FxHashSet}; -use scip::types as scip_types; +use scip::types::{self as scip_types, SymbolInformation}; use tracing::error; +use vfs::FileId; use crate::{ cli::flags, @@ -83,32 +85,56 @@ impl flags::Scip { text_document_encoding: scip_types::TextEncoding::UTF8.into(), special_fields: Default::default(), }; + let mut documents = Vec::new(); - let mut symbols_emitted: FxHashSet = FxHashSet::default(); - let mut tokens_to_symbol: FxHashMap = FxHashMap::default(); - let mut tokens_to_enclosing_symbol: FxHashMap> = - FxHashMap::default(); + // All TokenIds where an Occurrence has been emitted that references a symbol. + let mut token_ids_referenced: FxHashSet = FxHashSet::default(); + // All TokenIds where the SymbolInformation has been written to the document. + let mut token_ids_emitted: FxHashSet = FxHashSet::default(); + // All FileIds emitted as documents. + let mut file_ids_emitted: FxHashSet = FxHashSet::default(); + + // All non-local symbols encountered, for detecting duplicate symbol errors. + let mut nonlocal_symbols_emitted: FxHashSet = FxHashSet::default(); + // List of (source_location, symbol) for duplicate symbol errors to report. + let mut duplicate_symbol_errors: Vec<(String, String)> = Vec::new(); + // This is called after definitions have been deduplicated by token_ids_emitted. The purpose + // is to detect reuse of symbol names because this causes ambiguity about their meaning. + let mut record_error_if_symbol_already_used = + |symbol: String, + is_inherent_impl: bool, + relative_path: &str, + line_index: &LineIndex, + text_range: TextRange| { + let is_local = symbol.starts_with("local "); + if !is_local && !nonlocal_symbols_emitted.insert(symbol.clone()) { + if is_inherent_impl { + // FIXME: See #18772. Duplicate SymbolInformation for inherent impls is + // omitted. It would be preferable to emit them with numbers with + // disambiguation, but this is more complex to implement. + false + } else { + let source_location = + text_range_to_string(relative_path, line_index, text_range); + duplicate_symbol_errors.push((source_location, symbol)); + // Keep duplicate SymbolInformation. This behavior is preferred over + // omitting so that the issue might be visible within downstream tools. + true + } + } else { + true + } + }; + + // Generates symbols from token monikers. + let mut symbol_generator = SymbolGenerator::new(); for StaticIndexedFile { file_id, tokens, .. } in si.files { - let mut local_count = 0; - let mut new_local_symbol = || { - let new_symbol = scip::types::Symbol::new_local(local_count); - local_count += 1; + symbol_generator.clear_document_local_state(); - new_symbol - }; - - let relative_path = match get_relative_filepath(&vfs, &root, file_id) { - Some(relative_path) => relative_path, - None => continue, - }; - - let line_index = LineIndex { - index: db.line_index(file_id), - encoding: PositionEncoding::Utf8, - endings: LineEndings::Unix, - }; + let Some(relative_path) = get_relative_filepath(&vfs, &root, file_id) else { continue }; + let line_index = get_line_index(db, file_id); let mut occurrences = Vec::new(); let mut symbols = Vec::new(); @@ -116,71 +142,58 @@ impl flags::Scip { tokens.into_iter().for_each(|(text_range, id)| { let token = si.tokens.get(id).unwrap(); - let range = text_range_to_scip_range(&line_index, text_range); - let symbol = tokens_to_symbol - .entry(id) - .or_insert_with(|| { - let symbol = token - .moniker - .as_ref() - .map(moniker_to_symbol) - .unwrap_or_else(&mut new_local_symbol); - scip::symbol::format_symbol(symbol) - }) - .clone(); - let enclosing_symbol = tokens_to_enclosing_symbol - .entry(id) - .or_insert_with(|| { - token - .enclosing_moniker - .as_ref() - .map(moniker_to_symbol) - .map(scip::symbol::format_symbol) - }) - .clone(); + let (symbol, enclosing_symbol, is_inherent_impl) = + if let Some(TokenSymbols { symbol, enclosing_symbol, is_inherent_impl }) = + symbol_generator.token_symbols(id, token) + { + (symbol, enclosing_symbol, is_inherent_impl) + } else { + ("".to_owned(), None, false) + }; - let mut symbol_roles = Default::default(); - - if let Some(def) = token.definition { - // if the range of the def and the range of the token are the same, this must be the definition. - // they also must be in the same file. See https://github.com/rust-lang/rust-analyzer/pull/17988 - if def.file_id == file_id && def.range == text_range { - symbol_roles |= scip_types::SymbolRole::Definition as i32; - } - - if symbols_emitted.insert(id) { - let documentation = match &token.documentation { - Some(doc) => vec![doc.as_str().to_owned()], - None => vec![], - }; - - let position_encoding = - scip_types::PositionEncoding::UTF8CodeUnitOffsetFromLineStart.into(); - let signature_documentation = - token.signature.clone().map(|text| scip_types::Document { - relative_path: relative_path.clone(), - language: "rust".to_owned(), - text, - position_encoding, - ..Default::default() - }); - let symbol_info = scip_types::SymbolInformation { - symbol: symbol.clone(), - documentation, - relationships: Vec::new(), - special_fields: Default::default(), - kind: symbol_kind(token.kind).into(), - display_name: token.display_name.clone().unwrap_or_default(), - signature_documentation: signature_documentation.into(), - enclosing_symbol: enclosing_symbol.unwrap_or_default(), - }; - - symbols.push(symbol_info) + if !symbol.is_empty() { + let is_defined_in_this_document = match token.definition { + Some(def) => def.file_id == file_id, + _ => false, + }; + if is_defined_in_this_document { + if token_ids_emitted.insert(id) { + // token_ids_emitted does deduplication. This checks that this results + // in unique emitted symbols, as otherwise references are ambiguous. + let should_emit = record_error_if_symbol_already_used( + symbol.clone(), + is_inherent_impl, + relative_path.as_str(), + &line_index, + text_range, + ); + if should_emit { + symbols.push(compute_symbol_info( + symbol.clone(), + enclosing_symbol, + token, + )); + } + } + } else { + token_ids_referenced.insert(id); } } + // If the range of the def and the range of the token are the same, this must be the definition. + // they also must be in the same file. See https://github.com/rust-lang/rust-analyzer/pull/17988 + let is_definition = match token.definition { + Some(def) => def.file_id == file_id && def.range == text_range, + _ => false, + }; + + let mut symbol_roles = Default::default(); + if is_definition { + symbol_roles |= scip_types::SymbolRole::Definition as i32; + } + occurrences.push(scip_types::Occurrence { - range, + range: text_range_to_scip_range(&line_index, text_range), symbol, symbol_roles, override_documentation: Vec::new(), @@ -206,15 +219,63 @@ impl flags::Scip { position_encoding, special_fields: Default::default(), }); + if !file_ids_emitted.insert(file_id) { + panic!("Invariant violation: file emitted multiple times."); + } + } + + // Collect all symbols referenced by the files but not defined within them. + let mut external_symbols = Vec::new(); + for id in token_ids_referenced.difference(&token_ids_emitted) { + let id = *id; + let token = si.tokens.get(id).unwrap(); + + let Some(definition) = token.definition else { + break; + }; + + let file_id = definition.file_id; + let Some(relative_path) = get_relative_filepath(&vfs, &root, file_id) else { continue }; + let line_index = get_line_index(db, file_id); + let text_range = definition.range; + if file_ids_emitted.contains(&file_id) { + tracing::error!( + "Bug: definition at {} should have been in an SCIP document but was not.", + text_range_to_string(relative_path.as_str(), &line_index, text_range) + ); + continue; + } + + let TokenSymbols { symbol, enclosing_symbol, .. } = symbol_generator + .token_symbols(id, token) + .expect("To have been referenced, the symbol must be in the cache."); + + record_error_if_symbol_already_used( + symbol.clone(), + false, + relative_path.as_str(), + &line_index, + text_range, + ); + external_symbols.push(compute_symbol_info(symbol.clone(), enclosing_symbol, token)); } let index = scip_types::Index { metadata: Some(metadata).into(), documents, - external_symbols: Vec::new(), + external_symbols, special_fields: Default::default(), }; + if !duplicate_symbol_errors.is_empty() { + eprintln!("{}", DUPLICATE_SYMBOLS_MESSAGE); + for (source_location, symbol) in duplicate_symbol_errors { + eprintln!("{}", source_location); + eprintln!(" Duplicate symbol: {}", symbol); + eprintln!(); + } + } + let out_path = self.output.unwrap_or_else(|| PathBuf::from(r"index.scip")); scip::write_message_to_file(out_path, index) .map_err(|err| anyhow::format_err!("Failed to write scip to file: {}", err))?; @@ -224,6 +285,53 @@ impl flags::Scip { } } +// FIXME: Known buggy cases are described here. +const DUPLICATE_SYMBOLS_MESSAGE: &str = " +Encountered duplicate scip symbols, indicating an internal rust-analyzer bug. These duplicates are +included in the output, but this causes information lookup to be ambiguous and so information about +these symbols presented by downstream tools may be incorrect. + +Known rust-analyzer bugs that can cause this: + + * Definitions in crate example binaries which have the same symbol as definitions in the library + or some other example. + + * Struct/enum/const/static/impl definitions nested in a function do not mention the function name. + See #18771. + +Duplicate symbols encountered: +"; + +fn compute_symbol_info( + symbol: String, + enclosing_symbol: Option, + token: &TokenStaticData, +) -> SymbolInformation { + let documentation = match &token.documentation { + Some(doc) => vec![doc.as_str().to_owned()], + None => vec![], + }; + + let position_encoding = scip_types::PositionEncoding::UTF8CodeUnitOffsetFromLineStart.into(); + let signature_documentation = token.signature.clone().map(|text| scip_types::Document { + relative_path: "".to_owned(), + language: "rust".to_owned(), + text, + position_encoding, + ..Default::default() + }); + scip_types::SymbolInformation { + symbol, + documentation, + relationships: Vec::new(), + special_fields: Default::default(), + kind: symbol_kind(token.kind).into(), + display_name: token.display_name.clone().unwrap_or_default(), + signature_documentation: signature_documentation.into(), + enclosing_symbol: enclosing_symbol.unwrap_or_default(), + } +} + fn get_relative_filepath( vfs: &vfs::Vfs, rootpath: &vfs::AbsPathBuf, @@ -232,6 +340,14 @@ fn get_relative_filepath( Some(vfs.file_path(file_id).as_path()?.strip_prefix(rootpath)?.as_str().to_owned()) } +fn get_line_index(db: &RootDatabase, file_id: FileId) -> LineIndex { + LineIndex { + index: db.line_index(file_id), + encoding: PositionEncoding::Utf8, + endings: LineEndings::Unix, + } +} + // SCIP Ranges have a (very large) optimization that ranges if they are on the same line // only encode as a vector of [start_line, start_col, end_col]. // @@ -247,6 +363,13 @@ fn text_range_to_scip_range(line_index: &LineIndex, range: TextRange) -> Vec String { + let LineCol { line: start_line, col: start_col } = line_index.index.line_col(range.start()); + let LineCol { line: end_line, col: end_col } = line_index.index.line_col(range.end()); + + format!("{relative_path}:{start_line}:{start_col}-{end_line}:{end_col}") +} + fn new_descriptor_str( name: &str, suffix: scip_types::descriptor::Suffix, @@ -259,14 +382,6 @@ fn new_descriptor_str( } } -fn new_descriptor(name: &str, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor { - if name.contains('\'') { - new_descriptor_str(&format!("`{name}`"), suffix) - } else { - new_descriptor_str(name, suffix) - } -} - fn symbol_kind(kind: SymbolInformationKind) -> scip_types::symbol_information::Kind { use scip_types::symbol_information::Kind as ScipKind; match kind { @@ -295,17 +410,91 @@ fn symbol_kind(kind: SymbolInformationKind) -> scip_types::symbol_information::K } } -fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol { - use scip_types::descriptor::Suffix::*; +#[derive(Clone)] +struct TokenSymbols { + symbol: String, + /// Definition that contains this one. Only set when `symbol` is local. + enclosing_symbol: Option, + /// True if this symbol is for an inherent impl. This is used to only emit `SymbolInformation` + /// for a struct's first inherent impl, since their symbol names are not disambiguated. + is_inherent_impl: bool, +} - let package_name = moniker.package_information.name.clone(); - let version = moniker.package_information.version.clone(); - let descriptors = moniker - .identifier +struct SymbolGenerator { + token_to_symbols: FxHashMap>, + local_count: usize, +} + +impl SymbolGenerator { + fn new() -> Self { + SymbolGenerator { token_to_symbols: FxHashMap::default(), local_count: 0 } + } + + fn clear_document_local_state(&mut self) { + self.local_count = 0; + } + + fn token_symbols(&mut self, id: TokenId, token: &TokenStaticData) -> Option { + let mut local_count = self.local_count; + let token_symbols = self + .token_to_symbols + .entry(id) + .or_insert_with(|| { + Some(match token.moniker.as_ref()? { + MonikerResult::Moniker(moniker) => TokenSymbols { + symbol: scip::symbol::format_symbol(moniker_to_symbol(moniker)), + enclosing_symbol: None, + is_inherent_impl: moniker + .identifier + .description + .get(moniker.identifier.description.len() - 2) + .is_some_and(|descriptor| { + descriptor.desc == MonikerDescriptorKind::Type + && descriptor.name == "impl" + }), + }, + MonikerResult::Local { enclosing_moniker } => { + let local_symbol = scip::types::Symbol::new_local(local_count); + local_count += 1; + TokenSymbols { + symbol: scip::symbol::format_symbol(local_symbol), + enclosing_symbol: enclosing_moniker + .as_ref() + .map(moniker_to_symbol) + .map(scip::symbol::format_symbol), + is_inherent_impl: false, + } + } + }) + }) + .clone(); + self.local_count = local_count; + token_symbols + } +} + +fn moniker_to_symbol(moniker: &Moniker) -> scip_types::Symbol { + scip_types::Symbol { + scheme: "rust-analyzer".into(), + package: Some(scip_types::Package { + manager: "cargo".to_owned(), + name: moniker.package_information.name.clone(), + version: moniker.package_information.version.clone().unwrap_or_else(|| ".".to_owned()), + special_fields: Default::default(), + }) + .into(), + descriptors: moniker_descriptors(&moniker.identifier), + special_fields: Default::default(), + } +} + +fn moniker_descriptors(identifier: &MonikerIdentifier) -> Vec { + use scip_types::descriptor::Suffix::*; + identifier .description .iter() .map(|desc| { - new_descriptor( + new_descriptor_str( &desc.name, match desc.desc { MonikerDescriptorKind::Namespace => Namespace, @@ -319,27 +508,13 @@ fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol { }, ) }) - .collect(); - - scip_types::Symbol { - scheme: "rust-analyzer".into(), - package: Some(scip_types::Package { - manager: "cargo".to_owned(), - name: package_name, - version: version.unwrap_or_else(|| ".".to_owned()), - special_fields: Default::default(), - }) - .into(), - descriptors, - special_fields: Default::default(), - } + .collect() } #[cfg(test)] mod test { use super::*; use ide::{FilePosition, TextSize}; - use scip::symbol::format_symbol; use test_fixture::ChangeFixture; use vfs::VfsPath; @@ -376,7 +551,21 @@ mod test { for &(range, id) in &file.tokens { if range.contains(offset - TextSize::from(1)) { let token = si.tokens.get(id).unwrap(); - found_symbol = token.moniker.as_ref().map(moniker_to_symbol); + found_symbol = match token.moniker.as_ref() { + None => None, + Some(MonikerResult::Moniker(moniker)) => { + Some(scip::symbol::format_symbol(moniker_to_symbol(moniker))) + } + Some(MonikerResult::Local { enclosing_moniker: Some(moniker) }) => { + Some(format!( + "local enclosed by {}", + scip::symbol::format_symbol(moniker_to_symbol(moniker)) + )) + } + Some(MonikerResult::Local { enclosing_moniker: None }) => { + Some("unenclosed local".to_owned()) + } + }; break; } } @@ -388,9 +577,7 @@ mod test { } assert!(found_symbol.is_some(), "must have one symbol {found_symbol:?}"); - let res = found_symbol.unwrap(); - let formatted = format_symbol(res); - assert_eq!(formatted, expected); + assert_eq!(found_symbol.unwrap(), expected); } #[test] @@ -467,8 +654,7 @@ pub mod module { } } "#, - // "foo::module::MyTrait::MyType", - "rust-analyzer cargo foo 0.1.0 module/MyTrait#[MyType]", + "rust-analyzer cargo foo 0.1.0 module/MyTrait#MyType#", ); } @@ -489,8 +675,7 @@ pub mod module { } } "#, - // "foo::module::MyStruct::MyTrait::func", - "rust-analyzer cargo foo 0.1.0 module/MyStruct#MyTrait#func().", + "rust-analyzer cargo foo 0.1.0 module/impl#[MyStruct][MyTrait]func().", ); } @@ -526,7 +711,7 @@ pub mod example_mod { pub fn func(x$0: usize) {} } "#, - "rust-analyzer cargo foo 0.1.0 example_mod/func().(x)", + "local enclosed by rust-analyzer cargo foo 0.1.0 example_mod/func().", ); } @@ -546,7 +731,7 @@ pub mod example_mod { } } "#, - "rust-analyzer cargo foo 0.1.0 example_mod/func().(x)", + "local enclosed by rust-analyzer cargo foo 0.1.0 example_mod/func().", ); } @@ -566,7 +751,7 @@ pub mod example_mod { } } "#, - "", + "local enclosed by rust-analyzer cargo foo 0.1.0 module/func().", ); } @@ -609,7 +794,7 @@ pub mod example_mod { } #[test] - fn symbol_for_for_type_alias() { + fn symbol_for_type_alias() { check_symbol( r#" //- /workspace/lib.rs crate:main @@ -619,6 +804,70 @@ pub mod example_mod { ); } + // FIXME: This test represents current misbehavior. + #[test] + fn symbol_for_nested_function() { + check_symbol( + r#" + //- /workspace/lib.rs crate:main + pub fn func() { + pub fn inner_func$0() {} + } + "#, + "rust-analyzer cargo main . inner_func().", + // FIXME: This should be a local: + // "local enclosed by rust-analyzer cargo main . func().", + ); + } + + // FIXME: This test represents current misbehavior. + #[test] + fn symbol_for_struct_in_function() { + check_symbol( + r#" + //- /workspace/lib.rs crate:main + pub fn func() { + struct SomeStruct$0 {} + } + "#, + "rust-analyzer cargo main . SomeStruct#", + // FIXME: This should be a local: + // "local enclosed by rust-analyzer cargo main . func().", + ); + } + + // FIXME: This test represents current misbehavior. + #[test] + fn symbol_for_const_in_function() { + check_symbol( + r#" + //- /workspace/lib.rs crate:main + pub fn func() { + const SOME_CONST$0: u32 = 1; + } + "#, + "rust-analyzer cargo main . SOME_CONST.", + // FIXME: This should be a local: + // "local enclosed by rust-analyzer cargo main . func().", + ); + } + + // FIXME: This test represents current misbehavior. + #[test] + fn symbol_for_static_in_function() { + check_symbol( + r#" + //- /workspace/lib.rs crate:main + pub fn func() { + static SOME_STATIC$0: u32 = 1; + } + "#, + "rust-analyzer cargo main . SOME_STATIC.", + // FIXME: This should be a local: + // "local enclosed by rust-analyzer cargo main . func().", + ); + } + #[test] fn documentation_matches_doc_comment() { let s = "/// foo\nfn bar() {}"; 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 b06117f73835..30f0031905f1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -50,6 +50,14 @@ mod patch_old_style; // - Don't use abbreviations unless really necessary // - foo_command = overrides the subcommand, foo_overrideCommand allows full overwriting, extra args only applies for foo_command +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum MaxSubstitutionLength { + Hide, + #[serde(untagged)] + Limit(usize), +} + // Defines the server-side configuration of the rust-analyzer. We generate // *parts* of VS Code's `package.json` config from this. Run `cargo test` to // re-generate that file. @@ -111,6 +119,9 @@ config_data! { /// Whether to show `Run` action. Only applies when /// `#rust-analyzer.hover.actions.enable#` is set. hover_actions_run_enable: bool = true, + /// Whether to show `Update Test` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set. + hover_actions_updateTest_enable: bool = true, /// Whether to show documentation on hover. hover_documentation_enable: bool = true, @@ -119,6 +130,12 @@ config_data! { hover_documentation_keywords_enable: bool = true, /// Use markdown syntax for links on hover. hover_links_enable: bool = true, + /// Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis. + /// + /// This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters. + /// + /// The default is 20 characters. + hover_maxSubstitutionLength: Option = Some(MaxSubstitutionLength::Limit(20)), /// How to render the align information in a memory layout hover. hover_memoryLayout_alignment: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), /// Whether to show memory layout data on hover. @@ -229,6 +246,9 @@ config_data! { /// Whether to show `Run` lens. Only applies when /// `#rust-analyzer.lens.enable#` is set. lens_run_enable: bool = true, + /// Whether to show `Update Test` lens. Only applies when + /// `#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set. + lens_updateTest_enable: bool = true, /// Disable project auto-discovery in favor of explicitly specified set /// of projects. @@ -426,11 +446,32 @@ config_data! { /// Toggles the additional completions that automatically add imports when completed. /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. completion_autoimport_enable: bool = true, + /// A list of full paths to items to exclude from auto-importing completions. + /// + /// Traits in this list won't have their methods suggested in completions unless the trait + /// is in scope. + /// + /// You can either specify a string path which defaults to type "always" or use the more verbose + /// form `{ "path": "path::to::item", type: "always" }`. + /// + /// For traits the type "methods" can be used to only exclude the methods but not the trait itself. + /// + /// This setting also inherits `#rust-analyzer.completion.excludeTraits#`. + completion_autoimport_exclude: Vec = vec![ + AutoImportExclusion::Verbose { path: "core::borrow::Borrow".to_owned(), r#type: AutoImportExclusionType::Methods }, + AutoImportExclusion::Verbose { path: "core::borrow::BorrowMut".to_owned(), r#type: AutoImportExclusionType::Methods }, + ], /// Toggles the additional completions that automatically show method calls and field accesses /// with `self` prefixed to them when inside a method. completion_autoself_enable: bool = true, /// Whether to add parenthesis and argument snippets when completing function. completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments, + /// A list of full paths to traits whose methods to exclude from completion. + /// + /// Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`. + /// + /// Note that the trait themselves can still be completed. + completion_excludeTraits: Vec = Vec::new(), /// Whether to show full function/method signatures in completion docs. completion_fullFunctionSignatures_enable: bool = false, /// Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. @@ -554,15 +595,12 @@ config_data! { /// /// This option does not take effect until rust-analyzer is restarted. cargo_sysroot: Option = Some("discover".to_owned()), - /// How to query metadata for the sysroot crate. Using cargo metadata allows rust-analyzer - /// to analyze third-party dependencies of the standard libraries. - cargo_sysrootQueryMetadata: SysrootQueryMetadata = SysrootQueryMetadata::CargoMetadata, /// Relative path to the sysroot library sources. If left unset, this will default to /// `{cargo.sysroot}/lib/rustlib/src/rust/library`. /// /// This option does not take effect until rust-analyzer is restarted. cargo_sysrootSrc: Option = None, - /// Compilation target override (target triple). + /// Compilation target override (target tuple). // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work // than `checkOnSave_target` cargo_target: Option = None, @@ -854,7 +892,7 @@ impl Config { if let Some(mut json) = change.client_config_change { tracing::info!("updating config from JSON: {:#}", json); - if !(json.is_null() || json.as_object().map_or(false, |it| it.is_empty())) { + if !(json.is_null() || json.as_object().is_some_and(|it| it.is_empty())) { let mut json_errors = vec![]; let detached_files = get_field_json::>( &mut json, @@ -869,11 +907,18 @@ impl Config { patch_old_style::patch_json_for_outdated_configs(&mut json); + let mut json_errors = vec![]; + let snips = get_field_json::>( + &mut json, + &mut json_errors, + "completion_snippets_custom", + None, + ) + .unwrap_or(self.completion_snippets_custom().to_owned()); + // IMPORTANT : This holds as long as ` completion_snippets_custom` is declared `client`. config.snippets.clear(); - let snips = self.completion_snippets_custom().to_owned(); - for (name, def) in snips.iter() { if def.prefix.is_empty() && def.postfix.is_empty() { continue; @@ -1147,6 +1192,7 @@ pub struct LensConfig { // runnables pub run: bool, pub debug: bool, + pub update_test: bool, pub interpret: bool, // implementations @@ -1182,6 +1228,7 @@ impl LensConfig { pub fn any(&self) -> bool { self.run || self.debug + || self.update_test || self.implementations || self.method_refs || self.refs_adt @@ -1194,7 +1241,7 @@ impl LensConfig { } pub fn runnable(&self) -> bool { - self.run || self.debug + self.run || self.debug || self.update_test } pub fn references(&self) -> bool { @@ -1208,6 +1255,7 @@ pub struct HoverActionsConfig { pub references: bool, pub run: bool, pub debug: bool, + pub update_test: bool, pub goto_type_def: bool, } @@ -1217,6 +1265,7 @@ impl HoverActionsConfig { references: false, run: false, debug: false, + update_test: false, goto_type_def: false, }; @@ -1229,7 +1278,7 @@ impl HoverActionsConfig { } pub fn runnable(&self) -> bool { - self.run || self.debug + self.run || self.debug || self.update_test } } @@ -1417,7 +1466,7 @@ impl Config { CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned() } } - pub fn completion(&self, source_root: Option) -> CompletionConfig { + pub fn completion(&self, source_root: Option) -> CompletionConfig<'_> { let client_capability_fields = self.completion_resolve_support_properties(); CompletionConfig { enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(), @@ -1448,6 +1497,27 @@ impl Config { } else { CompletionFieldsToResolve::from_client_capabilities(&client_capability_fields) }, + exclude_flyimport: self + .completion_autoimport_exclude(source_root) + .iter() + .map(|it| match it { + AutoImportExclusion::Path(path) => { + (path.clone(), ide_completion::AutoImportExclusionType::Always) + } + AutoImportExclusion::Verbose { path, r#type } => ( + path.clone(), + match r#type { + AutoImportExclusionType::Always => { + ide_completion::AutoImportExclusionType::Always + } + AutoImportExclusionType::Methods => { + ide_completion::AutoImportExclusionType::Methods + } + }, + ), + }) + .collect(), + exclude_traits: self.completion_excludeTraits(source_root), } } @@ -1503,6 +1573,9 @@ impl Config { references: enable && self.hover_actions_references_enable().to_owned(), run: enable && self.hover_actions_run_enable().to_owned(), debug: enable && self.hover_actions_debug_enable().to_owned(), + update_test: enable + && self.hover_actions_run_enable().to_owned() + && self.hover_actions_updateTest_enable().to_owned(), goto_type_def: enable && self.hover_actions_gotoTypeDef_enable().to_owned(), } } @@ -1533,6 +1606,11 @@ impl Config { max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(), max_fields_count: self.hover_show_fields().to_owned(), max_enum_variants_count: self.hover_show_enumVariants().to_owned(), + max_subst_ty_len: match self.hover_maxSubstitutionLength() { + Some(MaxSubstitutionLength::Hide) => ide::SubstTyLen::Hide, + Some(MaxSubstitutionLength::Limit(limit)) => ide::SubstTyLen::LimitTo(*limit), + None => ide::SubstTyLen::Unlimited, + }, } } @@ -1860,12 +1938,6 @@ impl Config { }, target: self.cargo_target(source_root).clone(), sysroot, - sysroot_query_metadata: match self.cargo_sysrootQueryMetadata(None) { - SysrootQueryMetadata::CargoMetadata => { - project_model::SysrootQueryMetadata::CargoMetadata - } - SysrootQueryMetadata::None => project_model::SysrootQueryMetadata::None, - }, sysroot_src, rustc_source, cfg_overrides: project_model::CfgOverrides { @@ -1969,7 +2041,7 @@ impl Config { pub(crate) fn cargo_test_options(&self, source_root: Option) -> CargoOptions { CargoOptions { - target_triples: self.cargo_target(source_root).clone().into_iter().collect(), + target_tuples: self.cargo_target(source_root).clone().into_iter().collect(), all_targets: false, no_default_features: *self.cargo_noDefaultFeatures(source_root), all_features: matches!(self.cargo_features(source_root), CargoFeaturesDef::All), @@ -2004,7 +2076,7 @@ impl Config { Some(_) | None => FlycheckConfig::CargoCommand { command: self.check_command(source_root).clone(), options: CargoOptions { - target_triples: self + target_tuples: self .check_targets(source_root) .clone() .and_then(|targets| match &targets.0[..] { @@ -2047,11 +2119,13 @@ impl Config { fn target_dir_from_config(&self, source_root: Option) -> Option { self.cargo_targetDir(source_root).as_ref().and_then(|target_dir| match target_dir { TargetDirectory::UseSubdirectory(true) => { - Some(Utf8PathBuf::from("target/rust-analyzer")) + let env_var = env::var("CARGO_TARGET_DIR").ok(); + let mut path = Utf8PathBuf::from(env_var.as_deref().unwrap_or("target")); + path.push("rust-analyzer"); + Some(path) } TargetDirectory::UseSubdirectory(false) => None, - TargetDirectory::Directory(dir) if dir.is_relative() => Some(dir.clone()), - TargetDirectory::Directory(_) => None, + TargetDirectory::Directory(dir) => Some(dir.clone()), }) } @@ -2096,6 +2170,9 @@ impl Config { LensConfig { run: *self.lens_enable() && *self.lens_run_enable(), debug: *self.lens_enable() && *self.lens_debug_enable(), + update_test: *self.lens_enable() + && *self.lens_updateTest_enable() + && *self.lens_run_enable(), interpret: *self.lens_enable() && *self.lens_run_enable() && *self.interpret_tests(), implementations: *self.lens_enable() && *self.lens_implementations_enable(), method_refs: *self.lens_enable() && *self.lens_references_method_enable(), @@ -2377,6 +2454,21 @@ enum ExprFillDefaultDef { Default, } +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(untagged)] +#[serde(rename_all = "snake_case")] +pub enum AutoImportExclusion { + Path(String), + Verbose { path: String, r#type: AutoImportExclusionType }, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "snake_case")] +pub enum AutoImportExclusionType { + Always, + Methods, +} + #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum ImportGranularityDef { @@ -2568,13 +2660,6 @@ pub enum NumThreads { Concrete(usize), } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -#[serde(rename_all = "snake_case")] -pub enum SysrootQueryMetadata { - CargoMetadata, - None, -} - macro_rules! _default_val { (@verbatim: $s:literal, $ty:ty) => {{ let default_: $ty = serde_json::from_str(&$s).unwrap(); @@ -3426,13 +3511,45 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json } ] }, - "SysrootQueryMetadata" => set! { - "type": "string", - "enum": ["none", "cargo_metadata"], - "enumDescriptions": [ - "Do not query sysroot metadata, always use stitched sysroot.", - "Use `cargo metadata` to query sysroot metadata." - ], + "Option" => set! { + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": ["hide"] + }, + { + "type": "integer" + } + ] + }, + "Vec" => set! { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + }, + { + "type": "object", + "properties": { + "path": { + "type": "string", + }, + "type": { + "type": "string", + "enum": ["always", "methods"], + "enumDescriptions": [ + "Do not show this item or its methods (if it is a trait) in auto-import completions.", + "Do not show this traits methods in auto-import completions." + ], + }, + } + } + ] + } }, _ => panic!("missing entry for {ty}: {default} (field {field})"), } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs index c3ab7f3ae715..fafffa043f98 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -500,7 +500,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( fn rustc_code_description(code: Option<&str>) -> Option { code.filter(|code| { let mut chars = code.chars(); - chars.next().map_or(false, |c| c == 'E') + chars.next() == Some('E') && chars.by_ref().take(4).all(|c| c.is_ascii_digit()) && chars.next().is_none() }) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs index 96b164228efe..0c111319bb41 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs @@ -1,6 +1,6 @@ //! Infrastructure for lazy project discovery. Currently only support rust-project.json discovery //! via a custom discover command. -use std::{io, process::Command}; +use std::{io, path::Path}; use crossbeam_channel::Sender; use paths::{AbsPathBuf, Utf8Path, Utf8PathBuf}; @@ -43,7 +43,11 @@ impl DiscoverCommand { } /// Spawn the command inside [Discover] and report progress, if any. - pub(crate) fn spawn(&self, discover_arg: DiscoverArgument) -> io::Result { + pub(crate) fn spawn( + &self, + discover_arg: DiscoverArgument, + current_dir: &Path, + ) -> io::Result { let command = &self.command[0]; let args = &self.command[1..]; @@ -58,7 +62,7 @@ impl DiscoverCommand { }) .collect(); - let mut cmd = Command::new(command); + let mut cmd = toolchain::command(command, current_dir); cmd.args(args); Ok(DiscoverHandle { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 53c145f884e0..22f06d68d80d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -1,10 +1,11 @@ //! Flycheck provides the functionality needed to run `cargo check` to provide //! LSP diagnostics based on the output of the command. -use std::{fmt, io, mem, process::Command, time::Duration}; +use std::{fmt, io, process::Command, time::Duration}; use cargo_metadata::PackageId; use crossbeam_channel::{select_biased, unbounded, Receiver, Sender}; +use ide_db::FxHashSet; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::Deserialize as _; @@ -27,7 +28,7 @@ pub(crate) enum InvocationStrategy { #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct CargoOptions { - pub(crate) target_triples: Vec, + pub(crate) target_tuples: Vec, pub(crate) all_targets: bool, pub(crate) no_default_features: bool, pub(crate) all_features: bool, @@ -38,7 +39,7 @@ pub(crate) struct CargoOptions { pub(crate) target_dir: Option, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub(crate) enum Target { Bin(String), Example(String), @@ -48,7 +49,7 @@ pub(crate) enum Target { impl CargoOptions { pub(crate) fn apply_on_command(&self, cmd: &mut Command) { - for target in &self.target_triples { + for target in &self.target_tuples { cmd.args(["--target", target.as_str()]); } if self.all_targets { @@ -231,13 +232,9 @@ struct FlycheckActor { command_handle: Option>, /// The receiver side of the channel mentioned above. command_receiver: Option>, - package_status: FxHashMap, DiagnosticReceived>, -} - -#[derive(PartialEq, Eq, Copy, Clone, Debug)] -enum DiagnosticReceived { - Yes, - No, + diagnostics_cleared_for: FxHashSet>, + diagnostics_cleared_for_all: bool, + diagnostics_received: bool, } #[allow(clippy::large_enum_variant)] @@ -267,7 +264,9 @@ impl FlycheckActor { manifest_path, command_handle: None, command_receiver: None, - package_status: FxHashMap::default(), + diagnostics_cleared_for: Default::default(), + diagnostics_cleared_for_all: false, + diagnostics_received: false, } } @@ -344,23 +343,16 @@ impl FlycheckActor { error ); } - if self.package_status.is_empty() { + if !self.diagnostics_received { + tracing::trace!(flycheck_id = self.id, "clearing diagnostics"); // We finished without receiving any diagnostics. - // That means all of them are stale. + // Clear everything for good measure self.send(FlycheckMessage::ClearDiagnostics { id: self.id, package_id: None, }); - } else { - for (package_id, status) in mem::take(&mut self.package_status) { - if let DiagnosticReceived::No = status { - self.send(FlycheckMessage::ClearDiagnostics { - id: self.id, - package_id: Some(package_id), - }); - } - } } + self.clear_diagnostics_state(); self.report_progress(Progress::DidFinish(res)); } @@ -373,9 +365,18 @@ impl FlycheckActor { "artifact received" ); self.report_progress(Progress::DidCheckCrate(msg.target.name)); - self.package_status - .entry(Arc::new(msg.package_id)) - .or_insert(DiagnosticReceived::No); + let package_id = Arc::new(msg.package_id); + if self.diagnostics_cleared_for.insert(package_id.clone()) { + tracing::trace!( + flycheck_id = self.id, + package_id = package_id.repr, + "clearing diagnostics" + ); + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + package_id: Some(package_id), + }); + } } CargoCheckMessage::Diagnostic { diagnostic, package_id } => { tracing::trace!( @@ -384,15 +385,25 @@ impl FlycheckActor { package_id = package_id.as_ref().map(|it| &it.repr), "diagnostic received" ); + self.diagnostics_received = true; if let Some(package_id) = &package_id { - if !self.package_status.contains_key(package_id) { - self.package_status - .insert(package_id.clone(), DiagnosticReceived::Yes); + if self.diagnostics_cleared_for.insert(package_id.clone()) { + tracing::trace!( + flycheck_id = self.id, + package_id = package_id.repr, + "clearing diagnostics" + ); self.send(FlycheckMessage::ClearDiagnostics { id: self.id, package_id: Some(package_id.clone()), }); } + } else if !self.diagnostics_cleared_for_all { + self.diagnostics_cleared_for_all = true; + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + package_id: None, + }); } self.send(FlycheckMessage::AddDiagnostic { id: self.id, @@ -417,8 +428,14 @@ impl FlycheckActor { command_handle.cancel(); self.command_receiver.take(); self.report_progress(Progress::DidCancel); - self.package_status.clear(); } + self.clear_diagnostics_state(); + } + + fn clear_diagnostics_state(&mut self) { + self.diagnostics_cleared_for.clear(); + self.diagnostics_cleared_for_all = false; + self.diagnostics_received = false; } /// Construct a `Command` object for checking the user's code. If the user @@ -432,12 +449,11 @@ impl FlycheckActor { ) -> Option { match &self.config { FlycheckConfig::CargoCommand { command, options, ansi_color_output } => { - let mut cmd = Command::new(Tool::Cargo.path()); + let mut cmd = toolchain::command(Tool::Cargo.path(), &*self.root); if let Some(sysroot_root) = &self.sysroot_root { cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(sysroot_root)); } cmd.arg(command); - cmd.current_dir(&*self.root); match package { Some(pkg) => cmd.arg("-p").arg(pkg), @@ -462,7 +478,7 @@ impl FlycheckActor { if let Some(manifest_path) = &self.manifest_path { cmd.arg("--manifest-path"); cmd.arg(manifest_path); - if manifest_path.extension().map_or(false, |ext| ext == "rs") { + if manifest_path.extension() == Some("rs") { cmd.arg("-Zscript"); } } @@ -474,18 +490,15 @@ impl FlycheckActor { Some(cmd) } FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => { - let mut cmd = Command::new(command); - cmd.envs(extra_env); - - match invocation_strategy { - InvocationStrategy::Once => { - cmd.current_dir(&*self.root); - } + let root = match invocation_strategy { + InvocationStrategy::Once => &*self.root, InvocationStrategy::PerWorkspace => { - // FIXME: cmd.current_dir(&affected_workspace); - cmd.current_dir(&*self.root); + // FIXME: &affected_workspace + &*self.root } - } + }; + let mut cmd = toolchain::command(command, root); + cmd.envs(extra_env); // If the custom command has a $saved_file placeholder, and // we're saving a file, replace the placeholder in the arguments. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index dd13bdba4cb2..0f2d7823b7e7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -17,7 +17,7 @@ use parking_lot::{ MappedRwLockReadGuard, Mutex, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard, }; -use proc_macro_api::ProcMacroServer; +use proc_macro_api::ProcMacroClient; use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts}; use rustc_hash::{FxHashMap, FxHashSet}; use tracing::{span, trace, Level}; @@ -95,7 +95,7 @@ pub(crate) struct GlobalState { pub(crate) last_reported_status: lsp_ext::ServerStatusParams, // proc macros - pub(crate) proc_macro_clients: Arc<[anyhow::Result]>, + pub(crate) proc_macro_clients: Arc<[anyhow::Result]>, pub(crate) build_deps_changed: bool, // Flycheck @@ -726,7 +726,6 @@ impl GlobalStateSnapshot { }; return Some(TargetSpec::ProjectJson(ProjectJsonTargetSpec { - crate_id, label: build.label, target_kind: build.target_kind, shell_runnables: project.runnables().to_owned(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index 2aa4ffbe1dc1..ff50f7533a64 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -5,6 +5,7 @@ use std::{ }; use ide::Cancelled; +use ide_db::base_db::ra_salsa::Cycle; use lsp_server::{ExtractError, Response, ResponseError}; use serde::{de::DeserializeOwned, Serialize}; use stdx::thread::ThreadIntent; @@ -307,10 +308,31 @@ impl RequestDispatcher<'_> { } } +#[derive(Debug)] +enum HandlerCancelledError { + PropagatedPanic, + Inner(ide::Cancelled), +} + +impl std::error::Error for HandlerCancelledError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + HandlerCancelledError::PropagatedPanic => None, + HandlerCancelledError::Inner(cancelled) => Some(cancelled), + } + } +} + +impl fmt::Display for HandlerCancelledError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Cancelled") + } +} + fn thread_result_to_response( id: lsp_server::RequestId, result: thread::Result>, -) -> Result +) -> Result where R: lsp_types::request::Request, R::Params: DeserializeOwned, @@ -328,7 +350,13 @@ where if let Some(panic_message) = panic_message { message.push_str(": "); message.push_str(panic_message) - }; + } else if let Some(cycle) = panic.downcast_ref::() { + tracing::error!("Cycle propagated out of salsa! This is a bug: {cycle:?}"); + return Err(HandlerCancelledError::PropagatedPanic); + } else if let Ok(cancelled) = panic.downcast::() { + tracing::error!("Cancellation propagated out of salsa! This is a bug"); + return Err(HandlerCancelledError::Inner(*cancelled)); + } Ok(lsp_server::Response::new_err( id, @@ -342,7 +370,7 @@ where fn result_to_response( id: lsp_server::RequestId, result: anyhow::Result, -) -> Result +) -> Result where R: lsp_types::request::Request, R::Params: DeserializeOwned, @@ -353,7 +381,7 @@ where Err(e) => match e.downcast::() { Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message), Err(e) => match e.downcast::() { - Ok(cancelled) => return Err(cancelled), + Ok(cancelled) => return Err(HandlerCancelledError::Inner(cancelled)), Err(e) => lsp_server::Response::new_err( id, lsp_server::ErrorCode::InternalError as i32, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index c0231fd04e5d..98efc637c2c8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -10,7 +10,6 @@ use lsp_types::{ DidOpenTextDocumentParams, DidSaveTextDocumentParams, WorkDoneProgressCancelParams, }; use paths::Utf8PathBuf; -use stdx::TupleExt; use triomphe::Arc; use vfs::{AbsPathBuf, ChangeKind, VfsPath}; @@ -75,7 +74,6 @@ pub(crate) fn handle_did_open_text_document( tracing::error!("duplicate DidOpenTextDocument: {}", path); } - tracing::info!("New file content set {:?}", params.text_document.text); state.vfs.write().0.set_file_contents(path, Some(params.text_document.text.into_bytes())); if state.config.discover_workspace_config().is_some() { tracing::debug!("queuing task"); @@ -296,12 +294,11 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let may_flycheck_workspace = state.config.flycheck_workspace(None); let mut updated = false; let task = move || -> std::result::Result<(), ide::Cancelled> { - // Is the target binary? If so we let flycheck run only for the workspace that contains the crate. let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { let tgt_kind = it.target_kind(); - let (tgt_name, crate_id) = match it { - TargetSpec::Cargo(c) => (c.target, c.crate_id), - TargetSpec::ProjectJson(p) => (p.label, p.crate_id), + let (tgt_name, root, package) = match it { + TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package), + _ => return None, }; let tgt = match tgt_kind { @@ -309,28 +306,50 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { project_model::TargetKind::Example => Target::Example(tgt_name), project_model::TargetKind::Test => Target::Test(tgt_name), project_model::TargetKind::Bench => Target::Benchmark(tgt_name), - _ => return None, + _ => return Some((None, root, package)), }; - Some((tgt, crate_id)) + Some((Some(tgt), root, package)) }); - - let crate_ids = match target { - // Trigger flychecks for the only crate which the target belongs to - Some((_, krate)) => vec![krate], - None => { - // Trigger flychecks for all workspaces that depend on the saved file - // Crates containing or depending on the saved file - world - .analysis - .crates_for(file_id)? - .into_iter() - .flat_map(|id| world.analysis.transitive_rev_deps(id)) - .flatten() - .unique() - .collect::>() + tracing::debug!(?target, "flycheck target"); + // we have a specific non-library target, attempt to only check that target, nothing + // else will be affected + if let Some((target, root, package)) = target { + // trigger a package check if we have a non-library target as that can't affect + // anything else in the workspace OR if we're not allowed to check the workspace as + // the user opted into package checks then + let package_check_allowed = target.is_some() || !may_flycheck_workspace; + if package_check_allowed { + let workspace = + world.workspaces.iter().enumerate().find(|(_, ws)| match &ws.kind { + project_model::ProjectWorkspaceKind::Cargo { cargo, .. } + | project_model::ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _, _)), + .. + } => *cargo.workspace_root() == root, + _ => false, + }); + if let Some((idx, _)) = workspace { + world.flycheck[idx].restart_for_package(package, target); + } } - }; + } + + if !may_flycheck_workspace { + return Ok(()); + } + + // Trigger flychecks for all workspaces that depend on the saved file + // Crates containing or depending on the saved file + let crate_ids = world + .analysis + .crates_for(file_id)? + .into_iter() + .flat_map(|id| world.analysis.transitive_rev_deps(id)) + .flatten() + .unique() + .collect::>(); + tracing::debug!(?crate_ids, "flycheck crate ids"); let crate_root_paths: Vec<_> = crate_ids .iter() .filter_map(|&crate_id| { @@ -344,53 +363,41 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { }) .collect::>()?; let crate_root_paths: Vec<_> = crate_root_paths.iter().map(Deref::deref).collect(); + tracing::debug!(?crate_root_paths, "flycheck crate roots"); // Find all workspaces that have at least one target containing the saved file - let workspace_ids = world.workspaces.iter().enumerate().filter_map(|(idx, ws)| { - let package = match &ws.kind { + let workspace_ids = + world.workspaces.iter().enumerate().filter(|(_, ws)| match &ws.kind { project_model::ProjectWorkspaceKind::Cargo { cargo, .. } | project_model::ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. - } => cargo.packages().find_map(|pkg| { - let has_target_with_root = cargo[pkg] + } => cargo.packages().any(|pkg| { + cargo[pkg] .targets .iter() - .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path())); - has_target_with_root.then(|| cargo.package_flag(&cargo[pkg])) + .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path())) }), - project_model::ProjectWorkspaceKind::Json(project) => { - if !project.crates().any(|(_, krate)| { - crate_root_paths.contains(&krate.root_module.as_path()) - }) { - return None; - } - None - } - project_model::ProjectWorkspaceKind::DetachedFile { .. } => return None, - }; - Some((idx, package)) - }); + project_model::ProjectWorkspaceKind::Json(project) => project + .crates() + .any(|(_, krate)| crate_root_paths.contains(&krate.root_module.as_path())), + project_model::ProjectWorkspaceKind::DetachedFile { .. } => false, + }); let saved_file = vfs_path.as_path().map(|p| p.to_owned()); // Find and trigger corresponding flychecks 'flychecks: for flycheck in world.flycheck.iter() { - for (id, package) in workspace_ids.clone() { + for (id, _) in workspace_ids.clone() { if id == flycheck.id() { updated = true; - if may_flycheck_workspace { - flycheck.restart_workspace(saved_file.clone()) - } else if let Some(package) = package { - flycheck - .restart_for_package(package, target.clone().map(TupleExt::head)) - } + flycheck.restart_workspace(saved_file.clone()); continue 'flychecks; } } } // No specific flycheck was triggered, so let's trigger all of them. - if !updated && may_flycheck_workspace { + if !updated { for flycheck in world.flycheck.iter() { flycheck.restart_workspace(saved_file.clone()); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 8f2bf80ea26d..7ac70efe2d6e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1,12 +1,7 @@ //! This module is responsible for implementing handlers for Language Server //! Protocol. This module specifically handles requests. -use std::{ - fs, - io::Write as _, - ops::Not, - process::{self, Stdio}, -}; +use std::{fs, io::Write as _, ops::Not, process::Stdio}; use anyhow::Context; @@ -32,7 +27,7 @@ use paths::Utf8PathBuf; use project_model::{CargoWorkspace, ManifestPath, ProjectWorkspaceKind, TargetKind}; use serde_json::json; use stdx::{format_to, never}; -use syntax::{algo, ast, AstNode, TextRange, TextSize}; +use syntax::{TextRange, TextSize}; use triomphe::Arc; use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath}; @@ -933,39 +928,32 @@ pub(crate) fn handle_runnables( let offset = params.position.and_then(|it| from_proto::offset(&line_index, it).ok()); let target_spec = TargetSpec::for_file(&snap, file_id)?; - let expect_test = match offset { - Some(offset) => { - let source_file = snap.analysis.parse(file_id)?; - algo::find_node_at_offset::(source_file.syntax(), offset) - .and_then(|it| it.path()?.segment()?.name_ref()) - .map_or(false, |it| it.text() == "expect" || it.text() == "expect_file") - } - None => false, - }; - let mut res = Vec::new(); for runnable in snap.analysis.runnables(file_id)? { - if should_skip_for_offset(&runnable, offset) { - continue; - } - if should_skip_target(&runnable, target_spec.as_ref()) { + if should_skip_for_offset(&runnable, offset) + || should_skip_target(&runnable, target_spec.as_ref()) + { continue; } + + let update_test = runnable.update_test; if let Some(mut runnable) = to_proto::runnable(&snap, runnable)? { - if expect_test { - if let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args { - runnable.label = format!("{} + expect", runnable.label); - r.environment.insert("UPDATE_EXPECT".to_owned(), "1".to_owned()); - if let Some(TargetSpec::Cargo(CargoTargetSpec { - sysroot_root: Some(sysroot_root), - .. - })) = &target_spec - { - r.environment - .insert("RUSTC_TOOLCHAIN".to_owned(), sysroot_root.to_string()); - } - } + if let Some(runnable) = + to_proto::make_update_runnable(&runnable, &update_test.label(), &update_test.env()) + { + res.push(runnable); } + + if let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args { + if let Some(TargetSpec::Cargo(CargoTargetSpec { + sysroot_root: Some(sysroot_root), + .. + })) = &target_spec + { + r.environment.insert("RUSTC_TOOLCHAIN".to_owned(), sysroot_root.to_string()); + } + }; + res.push(runnable); } } @@ -1831,6 +1819,7 @@ pub(crate) fn handle_call_hierarchy_outgoing( let doc = TextDocumentIdentifier::new(item.uri); let frange = from_proto::file_range(&snap, &doc, item.selection_range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; + let line_index = snap.file_line_index(fpos.file_id)?; let config = snap.config.call_hierarchy(); let call_items = match snap.analysis.outgoing_calls(config, fpos)? { @@ -1841,8 +1830,6 @@ pub(crate) fn handle_call_hierarchy_outgoing( let mut res = vec![]; for call_item in call_items.into_iter() { - let file_id = call_item.target.file_id; - let line_index = snap.file_line_index(file_id)?; let item = to_proto::call_hierarchy_item(&snap, call_item.target)?; res.push(CallHierarchyOutgoingCall { to: item, @@ -2148,6 +2135,7 @@ fn runnable_action_links( } let title = runnable.title(); + let update_test = runnable.update_test; let r = to_proto::runnable(snap, runnable).ok()??; let mut group = lsp_ext::CommandLinkGroup::default(); @@ -2159,7 +2147,15 @@ fn runnable_action_links( if hover_actions_config.debug && client_commands_config.debug_single { let dbg_command = to_proto::command::debug_single(&r); - group.commands.push(to_command_link(dbg_command, r.label)); + group.commands.push(to_command_link(dbg_command, r.label.clone())); + } + + if hover_actions_config.update_test && client_commands_config.run_single { + let label = update_test.label(); + if let Some(r) = to_proto::make_update_runnable(&r, &label, &update_test.env()) { + let update_command = to_proto::command::run_single(&r, label.unwrap().as_str()); + group.commands.push(to_command_link(update_command, r.label.clone())); + } } Some(group) @@ -2243,10 +2239,31 @@ fn run_rustfmt( let line_index = snap.file_line_index(file_id)?; let source_root_id = snap.analysis.source_root_id(file_id).ok(); + // try to chdir to the file so we can respect `rustfmt.toml` + // FIXME: use `rustfmt --config-path` once + // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed + let current_dir = match text_document.uri.to_file_path() { + Ok(mut path) => { + // pop off file name + if path.pop() && path.is_dir() { + path + } else { + std::env::current_dir()? + } + } + Err(_) => { + tracing::error!( + text_document = ?text_document.uri, + "Unable to get path, rustfmt.toml might be ignored" + ); + std::env::current_dir()? + } + }; + let mut command = match snap.config.rustfmt(source_root_id) { RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => { // FIXME: Set RUSTUP_TOOLCHAIN - let mut cmd = process::Command::new(toolchain::Tool::Rustfmt.path()); + let mut cmd = toolchain::command(toolchain::Tool::Rustfmt.path(), current_dir); cmd.envs(snap.config.extra_env(source_root_id)); cmd.args(extra_args); @@ -2300,9 +2317,9 @@ fn run_rustfmt( } else { cmd }; - process::Command::new(cmd_path) + toolchain::command(cmd_path, current_dir) } - _ => process::Command::new(cmd), + _ => toolchain::command(cmd, current_dir), }; cmd.envs(snap.config.extra_env(source_root_id)); @@ -2313,24 +2330,6 @@ fn run_rustfmt( tracing::debug!(?command, "created format command"); - // try to chdir to the file so we can respect `rustfmt.toml` - // FIXME: use `rustfmt --config-path` once - // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed - match text_document.uri.to_file_path() { - Ok(mut path) => { - // pop off file name - if path.pop() && path.is_dir() { - command.current_dir(path); - } - } - Err(_) => { - tracing::error!( - text_document = ?text_document.uri, - "Unable to get path, rustfmt.toml might be ignored" - ); - } - } - let mut rustfmt = command .stdin(Stdio::piped()) .stdout(Stdio::piped()) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index 8946c7acb938..fcfd06679bf2 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 @@ -174,6 +174,8 @@ fn integrated_completion_benchmark() { limit: None, add_semicolon_to_unit: true, fields_to_resolve: CompletionFieldsToResolve::empty(), + exclude_flyimport: vec![], + exclude_traits: &[], }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -222,6 +224,8 @@ fn integrated_completion_benchmark() { limit: None, add_semicolon_to_unit: true, fields_to_resolve: CompletionFieldsToResolve::empty(), + exclude_flyimport: vec![], + exclude_traits: &[], }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -268,6 +272,8 @@ fn integrated_completion_benchmark() { limit: None, add_semicolon_to_unit: true, fields_to_resolve: CompletionFieldsToResolve::empty(), + exclude_flyimport: vec![], + exclude_traits: &[], }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index c0173d9c2470..f50cbba7acfe 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -1,5 +1,9 @@ //! rust-analyzer extensions to the LSP. +// Note when adding new resolve payloads, add a #[serde(default)] on boolean fields as some clients +// might strip `false` values from the JSON payload due to their reserialization logic turning false +// into null which will then cause them to be omitted in the resolve request. See https://github.com/rust-lang/rust-analyzer/issues/18767 + #![allow(clippy::disallowed_types)] use std::ops; @@ -427,14 +431,14 @@ impl Request for Runnables { const METHOD: &'static str = "experimental/runnables"; } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct RunnablesParams { pub text_document: TextDocumentIdentifier, pub position: Option, } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct Runnable { pub label: String, @@ -444,7 +448,7 @@ pub struct Runnable { pub args: RunnableArgs, } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] #[serde(untagged)] pub enum RunnableArgs { @@ -452,14 +456,14 @@ pub enum RunnableArgs { Shell(ShellRunnableArgs), } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "lowercase")] pub enum RunnableKind { Cargo, Shell, } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct CargoRunnableArgs { #[serde(skip_serializing_if = "FxHashMap::is_empty")] @@ -475,7 +479,7 @@ pub struct CargoRunnableArgs { pub executable_args: Vec, } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct ShellRunnableArgs { #[serde(skip_serializing_if = "FxHashMap::is_empty")] @@ -829,6 +833,7 @@ pub struct CompletionResolveData { pub version: Option, #[serde(skip_serializing_if = "Option::is_none", default)] pub trigger_character: Option, + #[serde(default)] pub for_ref: bool, pub hash: String, } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 05e93b4e6acf..fe4d02dcb4fc 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -20,6 +20,7 @@ use itertools::Itertools; use paths::{Utf8Component, Utf8Prefix}; use semver::VersionReq; use serde_json::to_value; +use syntax::SmolStr; use vfs::AbsPath; use crate::{ @@ -560,8 +561,7 @@ pub(crate) fn inlay_hint( let text_edits = if snap .config .visual_studio_code_version() - // https://github.com/microsoft/vscode/issues/193124 - .map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) + .is_none_or(|version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) && resolve_range_and_hash.is_some() && fields_to_resolve.resolve_text_edits { @@ -1567,6 +1567,7 @@ pub(crate) fn code_lens( let line_index = snap.file_line_index(run.nav.file_id)?; let annotation_range = range(&line_index, annotation.range); + let update_test = run.update_test; let title = run.title(); let can_debug = match run.kind { ide::RunnableKind::DocTest { .. } => false, @@ -1602,6 +1603,18 @@ pub(crate) fn code_lens( data: None, }) } + if lens_config.update_test && client_commands_config.run_single { + let label = update_test.label(); + let env = update_test.env(); + if let Some(r) = make_update_runnable(&r, &label, &env) { + let command = command::run_single(&r, label.unwrap().as_str()); + acc.push(lsp_types::CodeLens { + range: annotation_range, + command: Some(command), + data: None, + }) + } + } } if lens_config.interpret { @@ -1786,7 +1799,7 @@ pub(crate) mod command { pub(crate) fn debug_single(runnable: &lsp_ext::Runnable) -> lsp_types::Command { lsp_types::Command { - title: "Debug".into(), + title: "⚙\u{fe0e} Debug".into(), command: "rust-analyzer.debugSingle".into(), arguments: Some(vec![to_value(runnable).unwrap()]), } @@ -1838,6 +1851,28 @@ pub(crate) mod command { } } +pub(crate) fn make_update_runnable( + runnable: &lsp_ext::Runnable, + label: &Option, + env: &[(&str, &str)], +) -> Option { + if !matches!(runnable.args, lsp_ext::RunnableArgs::Cargo(_)) { + return None; + } + let label = label.as_ref()?; + + let mut runnable = runnable.clone(); + runnable.label = format!("{} + {}", runnable.label, label); + + let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args else { + unreachable!(); + }; + + r.environment.extend(env.iter().map(|(k, v)| (k.to_string(), v.to_string()))); + + Some(runnable) +} + pub(crate) fn implementation_title(count: usize) -> String { if count == 1 { "1 implementation".into() 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 d97d96d54a01..97657b926583 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 @@ -744,7 +744,8 @@ impl GlobalState { DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it), }; - let handle = discover.spawn(arg).unwrap(); + let handle = + discover.spawn(arg, &std::env::current_dir().unwrap()).unwrap(); self.discover_handle = Some(handle); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 3444773695b4..0add2cdf5a71 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -24,7 +24,7 @@ use ide_db::{ use itertools::Itertools; use load_cargo::{load_proc_macro, ProjectFolders}; use lsp_types::FileSystemWatcher; -use proc_macro_api::ProcMacroServer; +use proc_macro_api::ProcMacroClient; use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts}; use stdx::{format_to, thread::ThreadIntent}; use triomphe::Arc; @@ -630,13 +630,10 @@ impl GlobalState { }; let env: FxHashMap<_, _> = match &ws.kind { - ProjectWorkspaceKind::Cargo { cargo_config_extra_env, .. } - | ProjectWorkspaceKind::DetachedFile { - cargo: Some(_), - cargo_config_extra_env, - .. - } => cargo_config_extra_env - .iter() + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, ..)), .. } => cargo + .env() + .into_iter() .chain(self.config.extra_env(None)) .map(|(a, b)| (a.clone(), b.clone())) .chain( @@ -650,7 +647,7 @@ impl GlobalState { }; info!("Using proc-macro server at {path}"); - ProcMacroServer::spawn(&path, &env).map_err(|err| { + ProcMacroClient::spawn(&path, &env).map_err(|err| { tracing::error!( "Failed to run proc-macro server from path {path}, error: {err:?}", ); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index b4aa73d2780d..b28567fe09b5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -62,7 +62,6 @@ pub(crate) struct CargoTargetSpec { #[derive(Clone, Debug)] pub(crate) struct ProjectJsonTargetSpec { - pub(crate) crate_id: CrateId, pub(crate) label: String, pub(crate) target_kind: TargetKind, pub(crate) shell_runnables: Vec, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs index 2fd52547336e..503b3ee43a12 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs @@ -1,8 +1,6 @@ //! This module provides the functionality needed to run `cargo test` in a background //! thread and report the result of each test in a channel. -use std::process::Command; - use crossbeam_channel::Sender; use paths::AbsPath; use serde::Deserialize as _; @@ -78,7 +76,7 @@ impl CargoTestHandle { test_target: TestTarget, sender: Sender, ) -> std::io::Result { - let mut cmd = Command::new(Tool::Cargo.path()); + let mut cmd = toolchain::command(Tool::Cargo.path(), root); cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.arg("test"); 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 c1ca59606379..2b3c0a47a220 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 @@ -1082,11 +1082,11 @@ fn resolve_proc_macro() { return; } - let sysroot = project_model::Sysroot::discover( + let mut sysroot = project_model::Sysroot::discover( &AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()), &Default::default(), - project_model::SysrootQueryMetadata::CargoMetadata, ); + sysroot.load_workspace(&project_model::SysrootSourceWorkspaceConfig::default_cargo()); let proc_macro_server_path = sysroot.discover_proc_macro_srv().unwrap(); diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs index 20c3b087af5d..8dc957350381 100644 --- a/src/tools/rust-analyzer/crates/span/src/lib.rs +++ b/src/tools/rust-analyzer/crates/span/src/lib.rs @@ -358,6 +358,18 @@ impl HirFileId { } } +/// Legacy span type, only defined here as it is still used by the proc-macro server. +/// While rust-analyzer doesn't use this anymore at all, RustRover relies on the legacy type for +/// proc-macro expansion. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct TokenId(pub u32); + +impl std::fmt::Debug for TokenId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + #[cfg(not(feature = "ra-salsa"))] mod intern_id_proxy { use std::fmt; diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs index 3a05b83e4970..ed8b1908d601 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs @@ -5,17 +5,14 @@ use std::fmt; use intern::Symbol; use rustc_hash::{FxHashMap, FxHashSet}; use span::{Edition, SpanAnchor, SpanData, SpanMap}; -use stdx::{format_to, never, non_empty_vec::NonEmptyVec}; +use stdx::{format_to, never}; use syntax::{ ast::{self, make::tokens::doc_comment}, format_smolstr, AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, WalkEvent, T, }; -use tt::{ - buffer::{Cursor, TokenBuffer}, - token_to_literal, -}; +use tt::{buffer::Cursor, token_to_literal}; pub mod prettify_macro_expansion; mod to_parser_input; @@ -99,7 +96,7 @@ pub fn syntax_node_to_token_tree( map: SpanMap, span: SpanData, mode: DocCommentDesugarMode, -) -> tt::Subtree> +) -> tt::TopSubtree> where SpanData: Copy + fmt::Debug, SpanMap: SpanMapper>, @@ -118,7 +115,7 @@ pub fn syntax_node_to_token_tree_modified( remove: FxHashSet, call_site: SpanData, mode: DocCommentDesugarMode, -) -> tt::Subtree> +) -> tt::TopSubtree> where SpanMap: SpanMapper>, SpanData: Copy + fmt::Debug, @@ -142,7 +139,7 @@ where /// Converts a [`tt::Subtree`] back to a [`SyntaxNode`]. /// The produced `SpanMap` contains a mapping from the syntax nodes offsets to the subtree's spans. pub fn token_tree_to_syntax_node( - tt: &tt::Subtree>, + tt: &tt::TopSubtree>, entry_point: parser::TopEntryPoint, edition: parser::Edition, ) -> (Parse, SpanMap) @@ -150,16 +147,10 @@ where SpanData: Copy + fmt::Debug, Ctx: PartialEq, { - let buffer = match tt { - tt::Subtree { - delimiter: tt::Delimiter { kind: tt::DelimiterKind::Invisible, .. }, - token_trees, - } => TokenBuffer::from_tokens(token_trees), - _ => TokenBuffer::from_subtree(tt), - }; - let parser_input = to_parser_input(edition, &buffer); + let buffer = tt.view().strip_invisible(); + let parser_input = to_parser_input(edition, buffer); let parser_output = entry_point.parse(&parser_input, edition); - let mut tree_sink = TtTreeSink::new(buffer.begin()); + let mut tree_sink = TtTreeSink::new(buffer.cursor()); for event in parser_output.iter() { match event { parser::Step::Token { kind, n_input_tokens: n_raw_tokens } => { @@ -183,7 +174,7 @@ pub fn parse_to_token_tree( anchor: SpanAnchor, ctx: Ctx, text: &str, -) -> Option>> +) -> Option>> where SpanData: Copy + fmt::Debug, Ctx: Copy, @@ -202,7 +193,7 @@ pub fn parse_to_token_tree_static_span( edition: Edition, span: S, text: &str, -) -> Option> +) -> Option> where S: Copy + fmt::Debug, { @@ -215,47 +206,38 @@ where Some(convert_tokens(&mut conv)) } -fn convert_tokens(conv: &mut C) -> tt::Subtree +fn convert_tokens(conv: &mut C) -> tt::TopSubtree where C: TokenConverter, S: Copy + fmt::Debug, C::Token: fmt::Debug, { - let entry = tt::SubtreeBuilder { - delimiter: tt::Delimiter::invisible_spanned(conv.call_site()), - token_trees: vec![], - }; - let mut stack = NonEmptyVec::new(entry); + let mut builder = + tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(conv.call_site())); while let Some((token, abs_range)) = conv.bump() { - let tt::SubtreeBuilder { delimiter, token_trees } = stack.last_mut(); - + let delimiter = builder.expected_delimiter().map(|it| it.kind); let tt = match token.as_leaf() { - Some(leaf) => tt::TokenTree::Leaf(leaf.clone()), + Some(leaf) => leaf.clone(), None => match token.kind(conv) { // Desugar doc comments into doc attributes COMMENT => { let span = conv.span_for(abs_range); - if let Some(tokens) = conv.convert_doc_comment(&token, span) { - token_trees.extend(tokens); - } + conv.convert_doc_comment(&token, span, &mut builder); continue; } kind if kind.is_punct() && kind != UNDERSCORE => { - let expected = match delimiter.kind { - tt::DelimiterKind::Parenthesis => Some(T![')']), - tt::DelimiterKind::Brace => Some(T!['}']), - tt::DelimiterKind::Bracket => Some(T![']']), - tt::DelimiterKind::Invisible => None, + let expected = match delimiter { + Some(tt::DelimiterKind::Parenthesis) => Some(T![')']), + Some(tt::DelimiterKind::Brace) => Some(T!['}']), + Some(tt::DelimiterKind::Bracket) => Some(T![']']), + Some(tt::DelimiterKind::Invisible) | None => None, }; // Current token is a closing delimiter that we expect, fix up the closing span // and end the subtree here if matches!(expected, Some(expected) if expected == kind) { - if let Some(mut subtree) = stack.pop() { - subtree.delimiter.close = conv.span_for(abs_range); - stack.last_mut().token_trees.push(subtree.build().into()); - } + builder.close(conv.span_for(abs_range)); continue; } @@ -268,16 +250,7 @@ where // Start a new subtree if let Some(kind) = delim { - let open = conv.span_for(abs_range); - stack.push(tt::SubtreeBuilder { - delimiter: tt::Delimiter { - open, - // will be overwritten on subtree close above - close: open, - kind, - }, - token_trees: vec![], - }); + builder.open(kind, conv.span_for(abs_range)); continue; } @@ -289,7 +262,6 @@ where panic!("Token from lexer must be single char: token = {token:#?}") }; tt::Leaf::from(tt::Punct { char, spacing, span: conv.span_for(abs_range) }) - .into() } kind => { macro_rules! make_ident { @@ -320,7 +292,7 @@ where span: conv .span_for(TextRange::at(abs_range.start(), TextSize::of('\''))), }); - token_trees.push(apostrophe.into()); + builder.push(apostrophe); let ident = tt::Leaf::from(tt::Ident { sym: Symbol::intern(&token.to_text(conv)[1..]), @@ -330,47 +302,26 @@ where )), is_raw: tt::IdentIsRaw::No, }); - token_trees.push(ident.into()); + builder.push(ident); continue; } _ => continue, }; - leaf.into() + leaf } }, }; - token_trees.push(tt); + builder.push(tt); } // If we get here, we've consumed all input tokens. // We might have more than one subtree in the stack, if the delimiters are improperly balanced. // Merge them so we're left with one. - while let Some(entry) = stack.pop() { - let parent = stack.last_mut(); + builder.flatten_unclosed_subtrees(); - let leaf: tt::Leaf<_> = tt::Punct { - span: entry.delimiter.open, - char: match entry.delimiter.kind { - tt::DelimiterKind::Parenthesis => '(', - tt::DelimiterKind::Brace => '{', - tt::DelimiterKind::Bracket => '[', - tt::DelimiterKind::Invisible => '$', - }, - spacing: tt::Spacing::Alone, - } - .into(); - parent.token_trees.push(leaf.into()); - parent.token_trees.extend(entry.token_trees); - } - - let subtree = stack.into_last().build(); - if let [tt::TokenTree::Subtree(first)] = &*subtree.token_trees { - first.clone() - } else { - subtree - } + builder.build_skip_top_subtree() } fn is_single_token_op(kind: SyntaxKind) -> bool { @@ -436,25 +387,17 @@ fn convert_doc_comment( token: &syntax::SyntaxToken, span: S, mode: DocCommentDesugarMode, -) -> Option>> { - let comment = ast::Comment::cast(token.clone())?; - let doc = comment.kind().doc?; + builder: &mut tt::TopSubtreeBuilder, +) { + let Some(comment) = ast::Comment::cast(token.clone()) else { return }; + let Some(doc) = comment.kind().doc else { return }; let mk_ident = |s: &str| { - tt::TokenTree::from(tt::Leaf::from(tt::Ident { - sym: Symbol::intern(s), - span, - is_raw: tt::IdentIsRaw::No, - })) + tt::Leaf::from(tt::Ident { sym: Symbol::intern(s), span, is_raw: tt::IdentIsRaw::No }) }; - let mk_punct = |c: char| { - tt::TokenTree::from(tt::Leaf::from(tt::Punct { - char: c, - spacing: tt::Spacing::Alone, - span, - })) - }; + let mk_punct = + |c: char| tt::Leaf::from(tt::Punct { char: c, spacing: tt::Spacing::Alone, span }); let mk_doc_literal = |comment: &ast::Comment| { let prefix_len = comment.prefix().len(); @@ -467,24 +410,20 @@ fn convert_doc_comment( let (text, kind) = desugar_doc_comment_text(text, mode); let lit = tt::Literal { symbol: text, span, kind, suffix: None }; - tt::TokenTree::from(tt::Leaf::from(lit)) + tt::Leaf::from(lit) }; // Make `doc="\" Comments\"" - let meta_tkns = Box::new([mk_ident("doc"), mk_punct('='), mk_doc_literal(&comment)]); + let meta_tkns = [mk_ident("doc"), mk_punct('='), mk_doc_literal(&comment)]; // Make `#![]` - let mut token_trees = Vec::with_capacity(3); - token_trees.push(mk_punct('#')); + builder.push(mk_punct('#')); if let ast::CommentPlacement::Inner = doc { - token_trees.push(mk_punct('!')); + builder.push(mk_punct('!')); } - token_trees.push(tt::TokenTree::from(tt::Subtree { - delimiter: tt::Delimiter { open: span, close: span, kind: tt::DelimiterKind::Bracket }, - token_trees: meta_tkns, - })); - - Some(token_trees) + builder.open(tt::DelimiterKind::Bracket, span); + builder.extend(meta_tkns); + builder.close(span); } /// A raw token (straight from lexer) converter @@ -518,7 +457,12 @@ trait SrcToken { trait TokenConverter: Sized { type Token: SrcToken; - fn convert_doc_comment(&self, token: &Self::Token, span: S) -> Option>>; + fn convert_doc_comment( + &self, + token: &Self::Token, + span: S, + builder: &mut tt::TopSubtreeBuilder, + ); fn bump(&mut self) -> Option<(Self::Token, TextRange)>; @@ -567,9 +511,10 @@ where &self, &token: &usize, span: SpanData, - ) -> Option>>> { + builder: &mut tt::TopSubtreeBuilder>, + ) { let text = self.lexed.text(token); - convert_doc_comment(&doc_comment(text), span, self.mode) + convert_doc_comment(&doc_comment(text), span, self.mode, builder); } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { @@ -606,9 +551,9 @@ where { type Token = usize; - fn convert_doc_comment(&self, &token: &usize, span: S) -> Option>> { + fn convert_doc_comment(&self, &token: &usize, span: S, builder: &mut tt::TopSubtreeBuilder) { let text = self.lexed.text(token); - convert_doc_comment(&doc_comment(text), span, self.mode) + convert_doc_comment(&doc_comment(text), span, self.mode, builder); } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { @@ -773,8 +718,13 @@ where SpanMap: SpanMapper, { type Token = SynToken; - fn convert_doc_comment(&self, token: &Self::Token, span: S) -> Option>> { - convert_doc_comment(token.token(), span, self.mode) + fn convert_doc_comment( + &self, + token: &Self::Token, + span: S, + builder: &mut tt::TopSubtreeBuilder, + ) { + convert_doc_comment(token.token(), span, self.mode, builder); } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { @@ -899,15 +849,12 @@ where /// This occurs when a float literal is used as a field access. fn float_split(&mut self, has_pseudo_dot: bool) { let (text, span) = match self.cursor.token_tree() { - Some(tt::buffer::TokenTreeRef::Leaf( - tt::Leaf::Literal(tt::Literal { - symbol: text, - span, - kind: tt::LitKind::Float, - suffix: _, - }), - _, - )) => (text.as_str(), *span), + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { + symbol: text, + span, + kind: tt::LitKind::Float, + suffix: _, + }))) => (text.as_str(), *span), tt => unreachable!("{tt:?}"), }; // FIXME: Span splitting @@ -942,7 +889,7 @@ where } None => unreachable!(), } - self.cursor = self.cursor.bump(); + self.cursor.bump(); } fn token(&mut self, kind: SyntaxKind, mut n_tokens: u8) { @@ -950,24 +897,24 @@ where n_tokens = 2; } - let mut last = self.cursor; + let mut last_two = self.cursor.peek_two_leaves(); let mut combined_span = None; 'tokens: for _ in 0..n_tokens { let tmp: u8; if self.cursor.eof() { break; } - last = self.cursor; + last_two = self.cursor.peek_two_leaves(); let (text, span) = loop { break match self.cursor.token_tree() { - Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => match leaf { + Some(tt::TokenTree::Leaf(leaf)) => match leaf { tt::Leaf::Ident(ident) => { if ident.is_raw.yes() { self.buf.push_str("r#"); self.text_pos += TextSize::of("r#"); } let r = (ident.sym.as_str(), ident.span); - self.cursor = self.cursor.bump(); + self.cursor.bump(); r } tt::Leaf::Punct(punct) => { @@ -977,7 +924,7 @@ where std::str::from_utf8(std::slice::from_ref(&tmp)).unwrap(), punct.span, ); - self.cursor = self.cursor.bump(); + self.cursor.bump(); r } tt::Leaf::Literal(lit) => { @@ -989,20 +936,19 @@ where None => Some(lit.span), Some(prev_span) => Some(Self::merge_spans(prev_span, lit.span)), }; - self.cursor = self.cursor.bump(); + self.cursor.bump(); continue 'tokens; } }, - Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => { - self.cursor = self.cursor.subtree().unwrap(); + Some(tt::TokenTree::Subtree(subtree)) => { + self.cursor.bump(); match delim_to_str(subtree.delimiter.kind, false) { Some(it) => (it, subtree.delimiter.open), None => continue, } } None => { - let parent = self.cursor.end().unwrap(); - self.cursor = self.cursor.bump(); + let parent = self.cursor.end(); match delim_to_str(parent.delimiter.kind, true) { Some(it) => (it, parent.delimiter.close), None => continue, @@ -1023,12 +969,7 @@ where self.buf.clear(); // FIXME: Emitting whitespace for this is really just a hack, we should get rid of it. // Add whitespace between adjoint puncts - let next = last.bump(); - if let ( - Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(curr), _)), - Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(next), _)), - ) = (last.token_tree(), next.token_tree()) - { + if let Some([tt::Leaf::Punct(curr), tt::Leaf::Punct(next)]) = last_two { // Note: We always assume the semi-colon would be the last token in // other parts of RA such that we don't add whitespace here. // @@ -1058,7 +999,7 @@ where // We don't do what rustc does exactly, rustc does something clever when the spans have different syntax contexts // but this runs afoul of our separation between `span` and `hir-expand`. SpanData { - range: if a.ctx == b.ctx { + range: if a.ctx == b.ctx && a.anchor == b.anchor { TextRange::new( std::cmp::min(a.range.start(), b.range.start()), std::cmp::max(a.range.end(), b.range.end()), diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs index 7b8e3f2b49c2..d37cb508de19 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs @@ -2,10 +2,7 @@ use rustc_hash::FxHashMap; use span::Span; use syntax::{ast, AstNode}; use test_utils::extract_annotations; -use tt::{ - buffer::{TokenBuffer, TokenTreeRef}, - Leaf, Punct, Spacing, -}; +use tt::{buffer::Cursor, Leaf, Punct, Spacing}; use crate::{ dummy_test_span_utils::{DummyTestSpanMap, DUMMY}, @@ -32,22 +29,22 @@ fn check_punct_spacing(fixture: &str) { }) .collect(); - let buf = TokenBuffer::from_subtree(&subtree); - let mut cursor = buf.begin(); + let mut cursor = Cursor::new(&subtree.0); while !cursor.eof() { while let Some(token_tree) = cursor.token_tree() { - if let TokenTreeRef::Leaf( - Leaf::Punct(Punct { spacing, span: Span { range, .. }, .. }), - _, - ) = token_tree + if let tt::TokenTree::Leaf(Leaf::Punct(Punct { + spacing, + span: Span { range, .. }, + .. + })) = token_tree { if let Some(expected) = annotations.remove(range) { assert_eq!(expected, *spacing); } } - cursor = cursor.bump_subtree(); + cursor.bump(); } - cursor = cursor.bump(); + cursor.bump_or_end(); } assert!(annotations.is_empty(), "unchecked annotations: {annotations:?}"); diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs index 14216e309328..1bbb05f55075 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs @@ -6,37 +6,34 @@ use std::fmt; use span::Edition; use syntax::{SyntaxKind, SyntaxKind::*, T}; -use tt::buffer::TokenBuffer; - pub fn to_parser_input( edition: Edition, - buffer: &TokenBuffer<'_, S>, + buffer: tt::TokenTreesView<'_, S>, ) -> parser::Input { let mut res = parser::Input::default(); - let mut current = buffer.begin(); + let mut current = buffer.cursor(); while !current.eof() { - let cursor = current; - let tt = cursor.token_tree(); + let tt = current.token_tree(); // Check if it is lifetime - if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = tt { + if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = tt { if punct.char == '\'' { - let next = cursor.bump(); - match next.token_tree() { - Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(_ident), _)) => { + current.bump(); + match current.token_tree() { + Some(tt::TokenTree::Leaf(tt::Leaf::Ident(_ident))) => { res.push(LIFETIME_IDENT); - current = next.bump(); + current.bump(); continue; } - _ => panic!("Next token must be ident : {:#?}", next.token_tree()), + _ => panic!("Next token must be ident"), } } } - current = match tt { - Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => { + match tt { + Some(tt::TokenTree::Leaf(leaf)) => { match leaf { tt::Leaf::Literal(lit) => { let kind = match lit.kind { @@ -83,9 +80,9 @@ pub fn to_parser_input( } } } - cursor.bump() + current.bump(); } - Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => { + Some(tt::TokenTree::Subtree(subtree)) => { if let Some(kind) = match subtree.delimiter.kind { tt::DelimiterKind::Parenthesis => Some(T!['(']), tt::DelimiterKind::Brace => Some(T!['{']), @@ -94,22 +91,19 @@ pub fn to_parser_input( } { res.push(kind); } - cursor.subtree().unwrap() + current.bump(); } - None => match cursor.end() { - Some(subtree) => { - if let Some(kind) = match subtree.delimiter.kind { - tt::DelimiterKind::Parenthesis => Some(T![')']), - tt::DelimiterKind::Brace => Some(T!['}']), - tt::DelimiterKind::Bracket => Some(T![']']), - tt::DelimiterKind::Invisible => None, - } { - res.push(kind); - } - cursor.bump() + None => { + let subtree = current.end(); + if let Some(kind) = match subtree.delimiter.kind { + tt::DelimiterKind::Parenthesis => Some(T![')']), + tt::DelimiterKind::Brace => Some(T!['}']), + tt::DelimiterKind::Bracket => Some(T![']']), + tt::DelimiterKind::Invisible => None, + } { + res.push(kind); } - None => continue, - }, + } }; } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index 32b1f5f75448..72a46f2f9f00 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -42,6 +42,14 @@ pub use self::{ /// the same representation: a pointer to the tree root and a pointer to the /// node itself. pub trait AstNode { + /// This panics if the `SyntaxKind` is not statically known. + fn kind() -> SyntaxKind + where + Self: Sized, + { + panic!("dynamic `SyntaxKind` for `AstNode::kind()`") + } + fn can_cast(kind: SyntaxKind) -> bool where Self: Sized; diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index ffe9f16cfd52..291fc646e216 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -751,7 +751,7 @@ impl ast::MatchArmList { ted::insert_all(position, elements); fn needs_comma(arm: &ast::MatchArm) -> bool { - arm.expr().map_or(false, |e| !e.is_block_like()) && arm.comma_token().is_none() + arm.expr().is_some_and(|e| !e.is_block_like()) && arm.comma_token().is_none() } } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs index f3053f59836f..9466755576b6 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs @@ -393,7 +393,7 @@ impl ast::BlockExpr { FOR_EXPR | IF_EXPR => parent .children() .find(|it| ast::Expr::can_cast(it.kind())) - .map_or(true, |it| it == *self.syntax()), + .is_none_or(|it| it == *self.syntax()), LET_ELSE | FN | WHILE_EXPR | LOOP_EXPR | CONST_BLOCK_PAT => false, _ => true, } 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 3876ef71a077..69e2a9f9c1b2 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 @@ -2502,6 +2502,13 @@ pub struct AnyHasVisibility { } impl ast::HasVisibility for AnyHasVisibility {} impl AstNode for Abi { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ABI + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ABI } #[inline] @@ -2516,6 +2523,13 @@ impl AstNode for Abi { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ArgList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ARG_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ARG_LIST } #[inline] @@ -2530,6 +2544,13 @@ impl AstNode for ArgList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ArrayExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ARRAY_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ARRAY_EXPR } #[inline] @@ -2544,6 +2565,13 @@ impl AstNode for ArrayExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ArrayType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ARRAY_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ARRAY_TYPE } #[inline] @@ -2558,6 +2586,13 @@ impl AstNode for ArrayType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AsmClobberAbi { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_CLOBBER_ABI + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CLOBBER_ABI } #[inline] @@ -2572,6 +2607,13 @@ impl AstNode for AsmClobberAbi { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AsmConst { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_CONST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CONST } #[inline] @@ -2586,6 +2628,13 @@ impl AstNode for AsmConst { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AsmDirSpec { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_DIR_SPEC + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_DIR_SPEC } #[inline] @@ -2600,6 +2649,13 @@ impl AstNode for AsmDirSpec { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AsmExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_EXPR } #[inline] @@ -2614,6 +2670,13 @@ impl AstNode for AsmExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AsmLabel { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_LABEL + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_LABEL } #[inline] @@ -2628,6 +2691,13 @@ impl AstNode for AsmLabel { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AsmOperandExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_OPERAND_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_EXPR } #[inline] @@ -2642,6 +2712,13 @@ impl AstNode for AsmOperandExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AsmOperandNamed { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_OPERAND_NAMED + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_NAMED } #[inline] @@ -2656,6 +2733,13 @@ impl AstNode for AsmOperandNamed { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AsmOption { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_OPTION + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTION } #[inline] @@ -2670,6 +2754,13 @@ impl AstNode for AsmOption { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AsmOptions { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_OPTIONS + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTIONS } #[inline] @@ -2684,6 +2775,13 @@ impl AstNode for AsmOptions { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AsmRegOperand { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_REG_OPERAND + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_OPERAND } #[inline] @@ -2698,6 +2796,13 @@ impl AstNode for AsmRegOperand { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AsmRegSpec { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_REG_SPEC + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_SPEC } #[inline] @@ -2712,6 +2817,13 @@ impl AstNode for AsmRegSpec { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AsmSym { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASM_SYM + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_SYM } #[inline] @@ -2726,6 +2838,13 @@ impl AstNode for AsmSym { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AssocItemList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASSOC_ITEM_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_ITEM_LIST } #[inline] @@ -2740,6 +2859,13 @@ impl AstNode for AssocItemList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AssocTypeArg { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ASSOC_TYPE_ARG + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_TYPE_ARG } #[inline] @@ -2754,6 +2880,13 @@ impl AstNode for AssocTypeArg { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Attr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ATTR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ATTR } #[inline] @@ -2768,6 +2901,13 @@ impl AstNode for Attr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for AwaitExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + AWAIT_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == AWAIT_EXPR } #[inline] @@ -2782,6 +2922,13 @@ impl AstNode for AwaitExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for BecomeExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + BECOME_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == BECOME_EXPR } #[inline] @@ -2796,6 +2943,13 @@ impl AstNode for BecomeExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for BinExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + BIN_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == BIN_EXPR } #[inline] @@ -2810,6 +2964,13 @@ impl AstNode for BinExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for BlockExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + BLOCK_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == BLOCK_EXPR } #[inline] @@ -2824,6 +2985,13 @@ impl AstNode for BlockExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for BoxPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + BOX_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == BOX_PAT } #[inline] @@ -2838,6 +3006,13 @@ impl AstNode for BoxPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for BreakExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + BREAK_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == BREAK_EXPR } #[inline] @@ -2852,6 +3027,13 @@ impl AstNode for BreakExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for CallExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CALL_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CALL_EXPR } #[inline] @@ -2866,6 +3048,13 @@ impl AstNode for CallExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for CastExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CAST_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CAST_EXPR } #[inline] @@ -2880,6 +3069,13 @@ impl AstNode for CastExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ClosureBinder { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CLOSURE_BINDER + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CLOSURE_BINDER } #[inline] @@ -2894,6 +3090,13 @@ impl AstNode for ClosureBinder { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ClosureExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CLOSURE_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CLOSURE_EXPR } #[inline] @@ -2908,6 +3111,13 @@ impl AstNode for ClosureExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Const { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CONST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CONST } #[inline] @@ -2922,6 +3132,13 @@ impl AstNode for Const { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ConstArg { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CONST_ARG + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CONST_ARG } #[inline] @@ -2936,6 +3153,13 @@ impl AstNode for ConstArg { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ConstBlockPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CONST_BLOCK_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CONST_BLOCK_PAT } #[inline] @@ -2950,6 +3174,13 @@ impl AstNode for ConstBlockPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ConstParam { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CONST_PARAM + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CONST_PARAM } #[inline] @@ -2964,6 +3195,13 @@ impl AstNode for ConstParam { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ContinueExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + CONTINUE_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == CONTINUE_EXPR } #[inline] @@ -2978,6 +3216,13 @@ impl AstNode for ContinueExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for DynTraitType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + DYN_TRAIT_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == DYN_TRAIT_TYPE } #[inline] @@ -2992,6 +3237,13 @@ impl AstNode for DynTraitType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Enum { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ENUM + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ENUM } #[inline] @@ -3006,6 +3258,13 @@ impl AstNode for Enum { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ExprStmt { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + EXPR_STMT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_STMT } #[inline] @@ -3020,6 +3279,13 @@ impl AstNode for ExprStmt { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ExternBlock { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + EXTERN_BLOCK + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == EXTERN_BLOCK } #[inline] @@ -3034,6 +3300,13 @@ impl AstNode for ExternBlock { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ExternCrate { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + EXTERN_CRATE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == EXTERN_CRATE } #[inline] @@ -3048,6 +3321,13 @@ impl AstNode for ExternCrate { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ExternItemList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + EXTERN_ITEM_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == EXTERN_ITEM_LIST } #[inline] @@ -3062,6 +3342,13 @@ impl AstNode for ExternItemList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for FieldExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FIELD_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FIELD_EXPR } #[inline] @@ -3076,6 +3363,13 @@ impl AstNode for FieldExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Fn { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FN + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FN } #[inline] @@ -3090,6 +3384,13 @@ impl AstNode for Fn { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for FnPtrType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FN_PTR_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FN_PTR_TYPE } #[inline] @@ -3104,6 +3405,13 @@ impl AstNode for FnPtrType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ForExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FOR_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FOR_EXPR } #[inline] @@ -3118,6 +3426,13 @@ impl AstNode for ForExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ForType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FOR_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FOR_TYPE } #[inline] @@ -3132,6 +3447,13 @@ impl AstNode for ForType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for FormatArgsArg { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FORMAT_ARGS_ARG + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_ARG } #[inline] @@ -3146,6 +3468,13 @@ impl AstNode for FormatArgsArg { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for FormatArgsExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FORMAT_ARGS_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_EXPR } #[inline] @@ -3160,6 +3489,13 @@ impl AstNode for FormatArgsExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for GenericArgList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + GENERIC_ARG_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_ARG_LIST } #[inline] @@ -3174,6 +3510,13 @@ impl AstNode for GenericArgList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for GenericParamList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + GENERIC_PARAM_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_PARAM_LIST } #[inline] @@ -3188,6 +3531,13 @@ impl AstNode for GenericParamList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for IdentPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + IDENT_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == IDENT_PAT } #[inline] @@ -3202,6 +3552,13 @@ impl AstNode for IdentPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for IfExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + IF_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == IF_EXPR } #[inline] @@ -3216,6 +3573,13 @@ impl AstNode for IfExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Impl { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + IMPL + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == IMPL } #[inline] @@ -3230,6 +3594,13 @@ impl AstNode for Impl { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ImplTraitType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + IMPL_TRAIT_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == IMPL_TRAIT_TYPE } #[inline] @@ -3244,6 +3615,13 @@ impl AstNode for ImplTraitType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for IndexExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + INDEX_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == INDEX_EXPR } #[inline] @@ -3258,6 +3636,13 @@ impl AstNode for IndexExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for InferType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + INFER_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == INFER_TYPE } #[inline] @@ -3272,6 +3657,13 @@ impl AstNode for InferType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ItemList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + ITEM_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ITEM_LIST } #[inline] @@ -3286,6 +3678,13 @@ impl AstNode for ItemList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Label { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LABEL + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LABEL } #[inline] @@ -3300,6 +3699,13 @@ impl AstNode for Label { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for LetElse { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LET_ELSE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LET_ELSE } #[inline] @@ -3314,6 +3720,13 @@ impl AstNode for LetElse { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for LetExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LET_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LET_EXPR } #[inline] @@ -3328,6 +3741,13 @@ impl AstNode for LetExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for LetStmt { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LET_STMT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LET_STMT } #[inline] @@ -3342,6 +3762,13 @@ impl AstNode for LetStmt { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Lifetime { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LIFETIME + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LIFETIME } #[inline] @@ -3356,6 +3783,13 @@ impl AstNode for Lifetime { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for LifetimeArg { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LIFETIME_ARG + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LIFETIME_ARG } #[inline] @@ -3370,6 +3804,13 @@ impl AstNode for LifetimeArg { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for LifetimeParam { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LIFETIME_PARAM + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LIFETIME_PARAM } #[inline] @@ -3384,6 +3825,13 @@ impl AstNode for LifetimeParam { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Literal { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LITERAL + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LITERAL } #[inline] @@ -3398,6 +3846,13 @@ impl AstNode for Literal { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for LiteralPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LITERAL_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LITERAL_PAT } #[inline] @@ -3412,6 +3867,13 @@ impl AstNode for LiteralPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for LoopExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + LOOP_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == LOOP_EXPR } #[inline] @@ -3426,6 +3888,13 @@ impl AstNode for LoopExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for MacroCall { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_CALL + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_CALL } #[inline] @@ -3440,6 +3909,13 @@ impl AstNode for MacroCall { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for MacroDef { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_DEF + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_DEF } #[inline] @@ -3454,6 +3930,13 @@ impl AstNode for MacroDef { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for MacroExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_EXPR } #[inline] @@ -3468,6 +3951,13 @@ impl AstNode for MacroExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for MacroItems { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_ITEMS + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_ITEMS } #[inline] @@ -3482,6 +3972,13 @@ impl AstNode for MacroItems { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for MacroPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_PAT } #[inline] @@ -3496,6 +3993,13 @@ impl AstNode for MacroPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for MacroRules { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_RULES + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_RULES } #[inline] @@ -3510,6 +4014,13 @@ impl AstNode for MacroRules { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for MacroStmts { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_STMTS + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_STMTS } #[inline] @@ -3524,6 +4035,13 @@ impl AstNode for MacroStmts { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for MacroType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MACRO_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_TYPE } #[inline] @@ -3538,6 +4056,13 @@ impl AstNode for MacroType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for MatchArm { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MATCH_ARM + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_ARM } #[inline] @@ -3552,6 +4077,13 @@ impl AstNode for MatchArm { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for MatchArmList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MATCH_ARM_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_ARM_LIST } #[inline] @@ -3566,6 +4098,13 @@ impl AstNode for MatchArmList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for MatchExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MATCH_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_EXPR } #[inline] @@ -3580,6 +4119,13 @@ impl AstNode for MatchExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for MatchGuard { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MATCH_GUARD + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_GUARD } #[inline] @@ -3594,6 +4140,13 @@ impl AstNode for MatchGuard { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Meta { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + META + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == META } #[inline] @@ -3608,6 +4161,13 @@ impl AstNode for Meta { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for MethodCallExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + METHOD_CALL_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == METHOD_CALL_EXPR } #[inline] @@ -3622,6 +4182,13 @@ impl AstNode for MethodCallExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Module { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + MODULE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == MODULE } #[inline] @@ -3636,6 +4203,13 @@ impl AstNode for Module { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Name { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + NAME + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == NAME } #[inline] @@ -3650,6 +4224,13 @@ impl AstNode for Name { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for NameRef { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + NAME_REF + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == NAME_REF } #[inline] @@ -3664,6 +4245,13 @@ impl AstNode for NameRef { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for NeverType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + NEVER_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == NEVER_TYPE } #[inline] @@ -3678,6 +4266,13 @@ impl AstNode for NeverType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for OffsetOfExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + OFFSET_OF_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == OFFSET_OF_EXPR } #[inline] @@ -3692,6 +4287,13 @@ impl AstNode for OffsetOfExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for OrPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + OR_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == OR_PAT } #[inline] @@ -3706,6 +4308,13 @@ impl AstNode for OrPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Param { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PARAM + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PARAM } #[inline] @@ -3720,6 +4329,13 @@ impl AstNode for Param { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ParamList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PARAM_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PARAM_LIST } #[inline] @@ -3734,6 +4350,13 @@ impl AstNode for ParamList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ParenExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PAREN_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_EXPR } #[inline] @@ -3748,6 +4371,13 @@ impl AstNode for ParenExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ParenPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PAREN_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_PAT } #[inline] @@ -3762,6 +4392,13 @@ impl AstNode for ParenPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ParenType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PAREN_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_TYPE } #[inline] @@ -3776,6 +4413,13 @@ impl AstNode for ParenType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ParenthesizedArgList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PARENTHESIZED_ARG_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PARENTHESIZED_ARG_LIST } #[inline] @@ -3790,6 +4434,13 @@ impl AstNode for ParenthesizedArgList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Path { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATH + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PATH } #[inline] @@ -3804,6 +4455,13 @@ impl AstNode for Path { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for PathExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATH_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_EXPR } #[inline] @@ -3818,6 +4476,13 @@ impl AstNode for PathExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for PathPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATH_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_PAT } #[inline] @@ -3832,6 +4497,13 @@ impl AstNode for PathPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for PathSegment { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATH_SEGMENT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_SEGMENT } #[inline] @@ -3846,6 +4518,13 @@ impl AstNode for PathSegment { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for PathType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATH_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_TYPE } #[inline] @@ -3860,6 +4539,13 @@ impl AstNode for PathType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for PrefixExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PREFIX_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PREFIX_EXPR } #[inline] @@ -3874,6 +4560,13 @@ impl AstNode for PrefixExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for PtrType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PTR_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == PTR_TYPE } #[inline] @@ -3888,6 +4581,13 @@ impl AstNode for PtrType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RangeExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RANGE_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RANGE_EXPR } #[inline] @@ -3902,6 +4602,13 @@ impl AstNode for RangeExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RangePat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RANGE_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RANGE_PAT } #[inline] @@ -3916,6 +4623,13 @@ impl AstNode for RangePat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RecordExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_EXPR } #[inline] @@ -3930,6 +4644,13 @@ impl AstNode for RecordExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RecordExprField { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_EXPR_FIELD + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_EXPR_FIELD } #[inline] @@ -3944,6 +4665,13 @@ impl AstNode for RecordExprField { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RecordExprFieldList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_EXPR_FIELD_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_EXPR_FIELD_LIST } #[inline] @@ -3958,6 +4686,13 @@ impl AstNode for RecordExprFieldList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RecordField { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_FIELD + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_FIELD } #[inline] @@ -3972,6 +4707,13 @@ impl AstNode for RecordField { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RecordFieldList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_FIELD_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_FIELD_LIST } #[inline] @@ -3986,6 +4728,13 @@ impl AstNode for RecordFieldList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RecordPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT } #[inline] @@ -4000,6 +4749,13 @@ impl AstNode for RecordPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RecordPatField { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_PAT_FIELD + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT_FIELD } #[inline] @@ -4014,6 +4770,13 @@ impl AstNode for RecordPatField { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RecordPatFieldList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RECORD_PAT_FIELD_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT_FIELD_LIST } #[inline] @@ -4028,6 +4791,13 @@ impl AstNode for RecordPatFieldList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RefExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + REF_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == REF_EXPR } #[inline] @@ -4042,6 +4812,13 @@ impl AstNode for RefExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RefPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + REF_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == REF_PAT } #[inline] @@ -4056,6 +4833,13 @@ impl AstNode for RefPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RefType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + REF_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == REF_TYPE } #[inline] @@ -4070,6 +4854,13 @@ impl AstNode for RefType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Rename { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RENAME + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RENAME } #[inline] @@ -4084,6 +4875,13 @@ impl AstNode for Rename { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RestPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + REST_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == REST_PAT } #[inline] @@ -4098,6 +4896,13 @@ impl AstNode for RestPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for RetType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RET_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RET_TYPE } #[inline] @@ -4112,6 +4917,13 @@ impl AstNode for RetType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ReturnExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RETURN_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RETURN_EXPR } #[inline] @@ -4126,6 +4938,13 @@ impl AstNode for ReturnExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for ReturnTypeSyntax { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + RETURN_TYPE_SYNTAX + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == RETURN_TYPE_SYNTAX } #[inline] @@ -4140,6 +4959,13 @@ impl AstNode for ReturnTypeSyntax { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for SelfParam { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + SELF_PARAM + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == SELF_PARAM } #[inline] @@ -4154,6 +4980,13 @@ impl AstNode for SelfParam { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for SlicePat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + SLICE_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == SLICE_PAT } #[inline] @@ -4168,6 +5001,13 @@ impl AstNode for SlicePat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for SliceType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + SLICE_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == SLICE_TYPE } #[inline] @@ -4182,6 +5022,13 @@ impl AstNode for SliceType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for SourceFile { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + SOURCE_FILE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == SOURCE_FILE } #[inline] @@ -4196,6 +5043,13 @@ impl AstNode for SourceFile { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Static { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + STATIC + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == STATIC } #[inline] @@ -4210,6 +5064,13 @@ impl AstNode for Static { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for StmtList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + STMT_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST } #[inline] @@ -4224,6 +5085,13 @@ impl AstNode for StmtList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Struct { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + STRUCT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == STRUCT } #[inline] @@ -4238,6 +5106,13 @@ impl AstNode for Struct { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TokenTree { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TOKEN_TREE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TOKEN_TREE } #[inline] @@ -4252,6 +5127,13 @@ impl AstNode for TokenTree { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Trait { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TRAIT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TRAIT } #[inline] @@ -4266,6 +5148,13 @@ impl AstNode for Trait { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TraitAlias { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TRAIT_ALIAS + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TRAIT_ALIAS } #[inline] @@ -4280,6 +5169,13 @@ impl AstNode for TraitAlias { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TryExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TRY_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TRY_EXPR } #[inline] @@ -4294,6 +5190,13 @@ impl AstNode for TryExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TupleExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TUPLE_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_EXPR } #[inline] @@ -4308,6 +5211,13 @@ impl AstNode for TupleExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TupleField { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TUPLE_FIELD + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_FIELD } #[inline] @@ -4322,6 +5232,13 @@ impl AstNode for TupleField { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TupleFieldList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TUPLE_FIELD_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_FIELD_LIST } #[inline] @@ -4336,6 +5253,13 @@ impl AstNode for TupleFieldList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TuplePat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TUPLE_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_PAT } #[inline] @@ -4350,6 +5274,13 @@ impl AstNode for TuplePat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TupleStructPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TUPLE_STRUCT_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_STRUCT_PAT } #[inline] @@ -4364,6 +5295,13 @@ impl AstNode for TupleStructPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TupleType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TUPLE_TYPE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_TYPE } #[inline] @@ -4378,6 +5316,13 @@ impl AstNode for TupleType { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TypeAlias { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TYPE_ALIAS + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ALIAS } #[inline] @@ -4392,6 +5337,13 @@ impl AstNode for TypeAlias { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TypeArg { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TYPE_ARG + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ARG } #[inline] @@ -4406,6 +5358,13 @@ impl AstNode for TypeArg { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TypeBound { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TYPE_BOUND + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_BOUND } #[inline] @@ -4420,6 +5379,13 @@ impl AstNode for TypeBound { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TypeBoundList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TYPE_BOUND_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_BOUND_LIST } #[inline] @@ -4434,6 +5400,13 @@ impl AstNode for TypeBoundList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for TypeParam { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + TYPE_PARAM + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_PARAM } #[inline] @@ -4448,6 +5421,13 @@ impl AstNode for TypeParam { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for UnderscoreExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + UNDERSCORE_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == UNDERSCORE_EXPR } #[inline] @@ -4462,6 +5442,13 @@ impl AstNode for UnderscoreExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Union { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + UNION + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == UNION } #[inline] @@ -4476,6 +5463,13 @@ impl AstNode for Union { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Use { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + USE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == USE } #[inline] @@ -4490,6 +5484,13 @@ impl AstNode for Use { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for UseBoundGenericArgs { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + USE_BOUND_GENERIC_ARGS + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == USE_BOUND_GENERIC_ARGS } #[inline] @@ -4504,6 +5505,13 @@ impl AstNode for UseBoundGenericArgs { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for UseTree { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + USE_TREE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == USE_TREE } #[inline] @@ -4518,6 +5526,13 @@ impl AstNode for UseTree { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for UseTreeList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + USE_TREE_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == USE_TREE_LIST } #[inline] @@ -4532,6 +5547,13 @@ impl AstNode for UseTreeList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Variant { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + VARIANT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == VARIANT } #[inline] @@ -4546,6 +5568,13 @@ impl AstNode for Variant { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for VariantList { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + VARIANT_LIST + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == VARIANT_LIST } #[inline] @@ -4560,6 +5589,13 @@ impl AstNode for VariantList { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for Visibility { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + VISIBILITY + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == VISIBILITY } #[inline] @@ -4574,6 +5610,13 @@ impl AstNode for Visibility { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for WhereClause { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + WHERE_CLAUSE + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == WHERE_CLAUSE } #[inline] @@ -4588,6 +5631,13 @@ impl AstNode for WhereClause { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for WherePred { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + WHERE_PRED + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == WHERE_PRED } #[inline] @@ -4602,6 +5652,13 @@ impl AstNode for WherePred { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for WhileExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + WHILE_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == WHILE_EXPR } #[inline] @@ -4616,6 +5673,13 @@ impl AstNode for WhileExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for WildcardPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + WILDCARD_PAT + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == WILDCARD_PAT } #[inline] @@ -4630,6 +5694,13 @@ impl AstNode for WildcardPat { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for YeetExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + YEET_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == YEET_EXPR } #[inline] @@ -4644,6 +5715,13 @@ impl AstNode for YeetExpr { fn syntax(&self) -> &SyntaxNode { &self.syntax } } impl AstNode for YieldExpr { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + YIELD_EXPR + } #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == YIELD_EXPR } #[inline] diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index eb96ab6ef590..282cbc4b3a4a 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -8,7 +8,10 @@ //! Keep in mind that `from_text` functions should be kept private. The public //! API should require to assemble every node piecewise. The trick of //! `parse(format!())` we use internally is an implementation detail -- long -//! term, it will be replaced with direct tree manipulation. +//! term, it will be replaced with `quote!`. Do not add more usages to `from_text` - +//! use `quote!` instead. + +mod quote; use itertools::Itertools; use parser::{Edition, T}; @@ -16,7 +19,7 @@ use rowan::NodeOrToken; use stdx::{format_to, format_to_acc, never}; use crate::{ - ast::{self, Param}, + ast::{self, make::quote::quote, Param}, utils::is_raw_identifier, AstNode, SourceFile, SyntaxKind, SyntaxToken, }; @@ -118,7 +121,11 @@ pub fn name(name: &str) -> ast::Name { } pub fn name_ref(name_ref: &str) -> ast::NameRef { let raw_escape = raw_ident_esc(name_ref); - ast_from_text(&format!("fn f() {{ {raw_escape}{name_ref}; }}")) + quote! { + NameRef { + [IDENT format!("{raw_escape}{name_ref}")] + } + } } fn raw_ident_esc(ident: &str) -> &'static str { if is_raw_identifier(ident, Edition::CURRENT) { @@ -135,7 +142,11 @@ pub fn lifetime(text: &str) -> ast::Lifetime { tmp = format!("'{text}"); text = &tmp; } - ast_from_text(&format!("fn f<{text}>() {{ }}")) + quote! { + Lifetime { + [LIFETIME_IDENT text] + } + } } // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la @@ -175,63 +186,37 @@ pub fn ty_alias( where_clause: Option, assignment: Option<(ast::Type, Option)>, ) -> ast::TypeAlias { - let mut s = String::new(); - s.push_str(&format!("type {ident}")); - - if let Some(list) = generic_param_list { - s.push_str(&list.to_string()); - } - - if let Some(list) = type_param_bounds { - s.push_str(&format!(" : {list}")); - } - - if let Some(cl) = where_clause { - s.push_str(&format!(" {cl}")); - } - - if let Some(exp) = assignment { - if let Some(cl) = exp.1 { - s.push_str(&format!(" = {} {cl}", exp.0)); - } else { - s.push_str(&format!(" = {}", exp.0)); + let (assignment_ty, assignment_where) = assignment.unzip(); + let assignment_where = assignment_where.flatten(); + quote! { + TypeAlias { + [type] " " + Name { [IDENT ident] } + #generic_param_list + #(" " [:] " " #type_param_bounds)* + #(" " #where_clause)* + #(" " [=] " " #assignment_ty)* + #(" " #assignment_where)* + [;] } } - - s.push(';'); - ast_from_text(&s) } pub fn ty_fn_ptr>( - for_lifetime_list: Option, is_unsafe: bool, abi: Option, - params: I, + mut params: I, ret_type: Option, ) -> ast::FnPtrType { - let mut s = String::from("type __ = "); - - if let Some(list) = for_lifetime_list { - format_to!(s, "for{} ", list); + let is_unsafe = is_unsafe.then_some(()); + let first_param = params.next(); + quote! { + FnPtrType { + #(#is_unsafe [unsafe] " ")* #(#abi " ")* [fn] + ['('] #first_param #([,] " " #params)* [')'] + #(" " #ret_type)* + } } - - if is_unsafe { - s.push_str("unsafe "); - } - - if let Some(abi) = abi { - format_to!(s, "{} ", abi) - } - - s.push_str("fn"); - - format_to!(s, "({})", params.map(|p| p.to_string()).join(", ")); - - if let Some(ret_type) = ret_type { - format_to!(s, " {}", ret_type); - } - - ast_from_text(&s) } pub fn assoc_item_list() -> ast::AssocItemList { @@ -351,6 +336,24 @@ pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { ast_from_text(&format!("type __ = {name_ref};")) } +/// Type and expressions/patterns path differ in whether they require `::` before generic arguments. +/// Type paths allow them but they are often omitted, while expression/pattern paths require them. +pub fn generic_ty_path_segment( + name_ref: ast::NameRef, + generic_args: impl IntoIterator, +) -> ast::PathSegment { + let mut generic_args = generic_args.into_iter(); + let first_generic_arg = generic_args.next(); + quote! { + PathSegment { + #name_ref + GenericArgList { + [<] #first_generic_arg #([,] " " #generic_args)* [>] + } + } + } +} + pub fn path_segment_ty(type_ref: ast::Type, trait_ref: Option) -> ast::PathSegment { let text = match trait_ref { Some(trait_ref) => format!("fn f(x: <{type_ref} as {trait_ref}>) {{}}"), @@ -480,15 +483,16 @@ pub fn block_expr( stmts: impl IntoIterator, tail_expr: Option, ) -> ast::BlockExpr { - let mut buf = "{\n".to_owned(); - for stmt in stmts.into_iter() { - format_to!(buf, " {stmt}\n"); + quote! { + BlockExpr { + StmtList { + ['{'] "\n" + #(" " #stmts "\n")* + #(" " #tail_expr "\n")* + ['}'] + } + } } - if let Some(tail_expr) = tail_expr { - format_to!(buf, " {tail_expr}\n"); - } - buf += "}"; - ast_from_text(&format!("fn f() {buf}")) } pub fn async_move_block_expr( @@ -815,7 +819,7 @@ pub fn match_arm_with_guard( pub fn match_arm_list(arms: impl IntoIterator) -> ast::MatchArmList { let arms_str = arms.into_iter().fold(String::new(), |mut acc, arm| { - let needs_comma = arm.expr().map_or(true, |it| !it.is_block_like()); + let needs_comma = arm.expr().is_none_or(|it| !it.is_block_like()); let comma = if needs_comma { "," } else { "" }; let arm = arm.syntax(); format_to_acc!(acc, " {arm}{comma}\n") @@ -828,7 +832,7 @@ pub fn match_arm_list(arms: impl IntoIterator) -> ast::Mat } pub fn where_pred( - path: ast::Path, + path: ast::Type, bounds: impl IntoIterator, ) -> ast::WherePred { let bounds = bounds.into_iter().join(" + "); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make/quote.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make/quote.rs new file mode 100644 index 000000000000..300ef25c137c --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make/quote.rs @@ -0,0 +1,191 @@ +//! A `quote!`-like API for crafting AST nodes. + +pub(crate) use rowan::{GreenNode, GreenToken, NodeOrToken, SyntaxKind as RSyntaxKind}; + +macro_rules! quote_impl_ { + ( @append $children:ident ) => {}; // Base case. + + ( @append $children:ident + $node:ident { + $($tree:tt)* + } + $($rest:tt)* + ) => { + { + #[allow(unused_mut)] + let mut inner_children = ::std::vec::Vec::<$crate::ast::make::quote::NodeOrToken< + $crate::ast::make::quote::GreenNode, + $crate::ast::make::quote::GreenToken, + >>::new(); + $crate::ast::make::quote::quote_impl!( @append inner_children + $($tree)* + ); + let kind = <$crate::ast::$node as $crate::ast::AstNode>::kind(); + let node = $crate::ast::make::quote::GreenNode::new($crate::ast::make::quote::RSyntaxKind(kind as u16), inner_children); + $children.push($crate::ast::make::quote::NodeOrToken::Node(node)); + } + $crate::ast::make::quote::quote_impl!( @append $children $($rest)* ); + }; + + ( @append $children:ident + [ $token_kind:ident $token_text:expr ] + $($rest:tt)* + ) => { + $children.push($crate::ast::make::quote::NodeOrToken::Token( + $crate::ast::make::quote::GreenToken::new( + $crate::ast::make::quote::RSyntaxKind($crate::SyntaxKind::$token_kind as u16), + &$token_text, + ), + )); + $crate::ast::make::quote::quote_impl!( @append $children $($rest)* ); + }; + + ( @append $children:ident + [$($token:tt)+] + $($rest:tt)* + ) => { + $children.push($crate::ast::make::quote::NodeOrToken::Token( + $crate::ast::make::quote::GreenToken::new( + $crate::ast::make::quote::RSyntaxKind($crate::T![ $($token)+ ] as u16), + const { $crate::T![ $($token)+ ].text() }, + ), + )); + $crate::ast::make::quote::quote_impl!( @append $children $($rest)* ); + }; + + ( @append $children:ident + $whitespace:literal + $($rest:tt)* + ) => { + const { $crate::ast::make::quote::verify_only_whitespaces($whitespace) }; + $children.push($crate::ast::make::quote::NodeOrToken::Token( + $crate::ast::make::quote::GreenToken::new( + $crate::ast::make::quote::RSyntaxKind($crate::SyntaxKind::WHITESPACE as u16), + $whitespace, + ), + )); + $crate::ast::make::quote::quote_impl!( @append $children $($rest)* ); + }; + + ( @append $children:ident + # $var:ident + $($rest:tt)* + ) => { + $crate::ast::make::quote::ToNodeChild::append_node_child($var, &mut $children); + $crate::ast::make::quote::quote_impl!( @append $children $($rest)* ); + }; + + ( @append $children:ident + #( $($repetition:tt)+ )* + $($rest:tt)* + ) => { + $crate::ast::make::quote::quote_impl!( @extract_pounded_in_repetition $children + [] [] $($repetition)* + ); + $crate::ast::make::quote::quote_impl!( @append $children $($rest)* ); + }; + + // Base case - no repetition var. + ( @extract_pounded_in_repetition $children:ident + [ $($repetition:tt)* ] [ ] + ) => { + ::std::compile_error!("repetition in `ast::make::quote!()` without variable"); + }; + + // Base case - repetition var found. + ( @extract_pounded_in_repetition $children:ident + [ $($repetition:tt)* ] [ $repetition_var:ident ] + ) => { + ::std::iter::IntoIterator::into_iter($repetition_var).for_each(|$repetition_var| { + $crate::ast::make::quote::quote_impl!( @append $children $($repetition)* ); + }); + }; + + ( @extract_pounded_in_repetition $children:ident + [ $($repetition:tt)* ] [ $repetition_var1:ident ] # $repetition_var2:ident $($rest:tt)* + ) => { + ::std::compile_error!("repetition in `ast::make::quote!()` with more than one variable"); + }; + + ( @extract_pounded_in_repetition $children:ident + [ $($repetition:tt)* ] [ ] # $repetition_var:ident $($rest:tt)* + ) => { + $crate::ast::make::quote::quote_impl!( @extract_pounded_in_repetition $children + [ $($repetition)* # $repetition_var ] [ $repetition_var ] $($rest)* + ); + }; + + ( @extract_pounded_in_repetition $children:ident + [ $($repetition:tt)* ] [ $($repetition_var:tt)* ] $non_repetition_var:tt $($rest:tt)* + ) => { + $crate::ast::make::quote::quote_impl!( @extract_pounded_in_repetition $children + [ $($repetition)* $non_repetition_var ] [ $($repetition_var)* ] $($rest)* + ); + }; +} +pub(crate) use quote_impl_ as quote_impl; + +/// A `quote!`-like API for crafting AST nodes. +/// +/// Syntax: AST nodes are created with `Node { children }`, where `Node` is the node name in `ast` (`ast::Node`). +/// Tokens are creates with their syntax enclosed by brackets, e.g. `[::]` or `['{']`. Alternatively, tokens can +/// be created with the syntax `[token_kind token_text]`, where `token_kind` is a variant of `SyntaxKind` (e.g. +/// `IDENT`) and `token_text` is an expression producing `String` or `&str`. Whitespaces can be added +/// as string literals (i.e. `"\n "` is a whitespace token). Interpolation is allowed with `#` (`#variable`), +/// from `AstNode`s and `Option`s of them. Repetition is also supported, with only one repeating variable +/// and no separator (`#("\n" #variable [>])*`), for any `IntoIterator`. Note that `Option`s are also `IntoIterator`, +/// which can help when you want to conditionally include something along with an optional node. +/// +/// There needs to be one root node, and its type is returned. +/// +/// Be careful to closely match the Ungrammar AST, there is no validation for this! +macro_rules! quote_ { + ( $root:ident { $($tree:tt)* } ) => {{ + #[allow(unused_mut)] + let mut root = ::std::vec::Vec::<$crate::ast::make::quote::NodeOrToken< + $crate::ast::make::quote::GreenNode, + $crate::ast::make::quote::GreenToken, + >>::with_capacity(1); + $crate::ast::make::quote::quote_impl!( @append root $root { $($tree)* } ); + let root = root.into_iter().next().unwrap(); + let root = $crate::SyntaxNode::new_root(root.into_node().unwrap()); + <$crate::ast::$root as $crate::ast::AstNode>::cast(root).unwrap() + }}; +} +pub(crate) use quote_ as quote; + +use crate::AstNode; + +pub(crate) trait ToNodeChild { + fn append_node_child(self, children: &mut Vec>); +} + +impl ToNodeChild for N { + fn append_node_child(self, children: &mut Vec>) { + children.push((*self.syntax().clone_subtree().green()).to_owned().into()); + } +} + +impl ToNodeChild for Option { + fn append_node_child(self, children: &mut Vec>) { + if let Some(child) = self { + child.append_node_child(children); + } + } +} + +// This is useful when you want conditionally, based on some `bool`, to emit some code. +impl ToNodeChild for () { + fn append_node_child(self, _children: &mut Vec>) {} +} + +pub(crate) const fn verify_only_whitespaces(text: &str) { + let text = text.as_bytes(); + let mut i = 0; + while i < text.len() { + if !text[i].is_ascii_whitespace() { + panic!("non-whitespace found in whitespace token"); + } + i += 1; + } +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 6ec73e76f78d..81c7e15bcbc4 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -333,7 +333,7 @@ impl ast::Path { impl ast::Use { pub fn is_simple_glob(&self) -> bool { - self.use_tree().map_or(false, |use_tree| { + self.use_tree().is_some_and(|use_tree| { use_tree.use_tree_list().is_none() && use_tree.star_token().is_some() }) } @@ -387,7 +387,7 @@ impl ast::UseTreeList { if let Some((single_subtree,)) = u.use_trees().collect_tuple() { // We have a single subtree, check whether it is self. - let is_self = single_subtree.path().as_ref().map_or(false, |path| { + let is_self = single_subtree.path().as_ref().is_some_and(|path| { path.segment().and_then(|seg| seg.self_token()).is_some() && path.qualifier().is_none() }); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 280c5c25cb95..bea6bfeafcf2 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -1,6 +1,4 @@ //! Wrappers over [`make`] constructors -use itertools::Itertools; - use crate::{ ast::{self, make, HasGenericParams, HasName, HasTypeBounds, HasVisibility}, syntax_editor::SyntaxMappingBuilder, @@ -62,13 +60,12 @@ impl SyntaxFactory { pub fn block_expr( &self, - stmts: impl IntoIterator, + statements: impl IntoIterator, tail_expr: Option, ) -> ast::BlockExpr { - let stmts = stmts.into_iter().collect_vec(); - let mut input = stmts.iter().map(|it| it.syntax().clone()).collect_vec(); + let (statements, mut input) = iterator_input(statements); - let ast = make::block_expr(stmts, tail_expr.clone()).clone_for_update(); + let ast = make::block_expr(statements, tail_expr.clone()).clone_for_update(); if let Some(mut mapping) = self.mappings() { let stmt_list = ast.stmt_list().unwrap(); @@ -257,14 +254,15 @@ impl SyntaxFactory { pub fn turbofish_generic_arg_list( &self, - args: impl IntoIterator + Clone, + generic_args: impl IntoIterator, ) -> ast::GenericArgList { - let ast = make::turbofish_generic_arg_list(args.clone()).clone_for_update(); + let (generic_args, input) = iterator_input(generic_args); + let ast = make::turbofish_generic_arg_list(generic_args.clone()).clone_for_update(); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); builder.map_children( - args.into_iter().map(|arg| arg.syntax().clone()), + input.into_iter(), ast.generic_args().map(|arg| arg.syntax().clone()), ); builder.finish(&mut mapping); @@ -277,8 +275,7 @@ impl SyntaxFactory { &self, fields: impl IntoIterator, ) -> ast::RecordFieldList { - let fields: Vec = fields.into_iter().collect(); - let input: Vec<_> = fields.iter().map(|it| it.syntax().clone()).collect(); + let (fields, input) = iterator_input(fields); let ast = make::record_field_list(fields).clone_for_update(); if let Some(mut mapping) = self.mappings() { @@ -323,8 +320,7 @@ impl SyntaxFactory { &self, fields: impl IntoIterator, ) -> ast::TupleFieldList { - let fields: Vec = fields.into_iter().collect(); - let input: Vec<_> = fields.iter().map(|it| it.syntax().clone()).collect(); + let (fields, input) = iterator_input(fields); let ast = make::tuple_field_list(fields).clone_for_update(); if let Some(mut mapping) = self.mappings() { @@ -419,8 +415,7 @@ impl SyntaxFactory { &self, variants: impl IntoIterator, ) -> ast::VariantList { - let variants: Vec = variants.into_iter().collect(); - let input: Vec<_> = variants.iter().map(|it| it.syntax().clone()).collect(); + let (variants, input) = iterator_input(variants); let ast = make::variant_list(variants).clone_for_update(); if let Some(mut mapping) = self.mappings() { @@ -481,7 +476,7 @@ impl SyntaxFactory { pub fn token_tree( &self, delimiter: SyntaxKind, - tt: Vec>, + tt: impl IntoIterator>, ) -> ast::TokenTree { let tt: Vec<_> = tt.into_iter().collect(); let input: Vec<_> = tt.iter().cloned().filter_map(only_nodes).collect(); @@ -512,3 +507,20 @@ impl SyntaxFactory { make::tokens::whitespace(text) } } + +// We need to collect `input` here instead of taking `impl IntoIterator + Clone`, +// because if we took `impl IntoIterator + Clone`, that could be something like an +// `Iterator::map` with a closure that also makes use of a `SyntaxFactory` constructor. +// +// In that case, the iterator would be evaluated inside of the call to `map_children`, +// and the inner constructor would try to take a mutable borrow of the mappings `RefCell`, +// which would panic since it's already being mutably borrowed in the outer constructor. +fn iterator_input(input: impl IntoIterator) -> (Vec, Vec) { + input + .into_iter() + .map(|it| { + let syntax = it.syntax().clone(); + (it, syntax) + }) + .collect() +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index df017ddde643..7d5ca2704354 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -116,7 +116,7 @@ impl CommentKind { impl ast::Whitespace { pub fn spans_multiple_lines(&self) -> bool { let text = self.text(); - text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) + text.find('\n').is_some_and(|idx| text[idx + 1..].contains('\n')) } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index 8069fdd06f74..450d601615ee 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -24,7 +24,7 @@ impl SyntaxEditor { if last_param .syntax() .next_sibling_or_token() - .map_or(false, |it| it.kind() == SyntaxKind::COMMA) + .is_some_and(|it| it.kind() == SyntaxKind::COMMA) { self.insert( Position::after(last_param.syntax()), diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index 889a7d10adad..0e72d796875c 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -13,16 +13,18 @@ use hir_expand::{ proc_macro::{ ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacrosBuilder, }, - quote, FileRange, + quote, + tt::{Leaf, TokenTree, TopSubtree, TopSubtreeBuilder, TtElement, TtIter}, + FileRange, }; use intern::Symbol; use rustc_hash::FxHashMap; use span::{Edition, EditionedFileId, FileId, Span}; +use stdx::itertools::Itertools; use test_utils::{ extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER, }; -use tt::{Leaf, Subtree, TokenTree}; pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0); @@ -374,7 +376,7 @@ impl ChangeFixture { } } -fn default_test_proc_macros() -> [(String, ProcMacro); 6] { +fn default_test_proc_macros() -> [(String, ProcMacro); 8] { [ ( r#" @@ -466,6 +468,36 @@ pub fn issue_18089(_attr: TokenStream, _item: TokenStream) -> TokenStream { disabled: false, }, ), + ( + r#" +#[proc_macro_attribute] +pub fn issue_18840(_attr: TokenStream, _item: TokenStream) -> TokenStream { + loop {} +} +"# + .into(), + ProcMacro { + name: Symbol::intern("issue_18840"), + kind: ProcMacroKind::Attr, + expander: sync::Arc::new(Issue18840ProcMacroExpander), + disabled: false, + }, + ), + ( + r#" +#[proc_macro] +pub fn issue_17479(input: TokenStream) -> TokenStream { + input +} +"# + .into(), + ProcMacro { + name: Symbol::intern("issue_17479"), + kind: ProcMacroKind::Bang, + expander: sync::Arc::new(Issue17479ProcMacroExpander), + disabled: false, + }, + ), ] } @@ -580,14 +612,14 @@ struct IdentityProcMacroExpander; impl ProcMacroExpander for IdentityProcMacroExpander { fn expand( &self, - subtree: &Subtree, - _: Option<&Subtree>, + subtree: &TopSubtree, + _: Option<&TopSubtree>, _: &Env, _: Span, _: Span, _: Span, _: Option, - ) -> Result, ProcMacroExpansionError> { + ) -> Result { Ok(subtree.clone()) } } @@ -598,15 +630,17 @@ struct Issue18089ProcMacroExpander; impl ProcMacroExpander for Issue18089ProcMacroExpander { fn expand( &self, - subtree: &Subtree, - _: Option<&Subtree>, + subtree: &TopSubtree, + _: Option<&TopSubtree>, _: &Env, _: Span, call_site: Span, _: Span, _: Option, - ) -> Result, ProcMacroExpansionError> { - let macro_name = &subtree.token_trees[1]; + ) -> Result { + let tt::TokenTree::Leaf(macro_name) = &subtree.0[2] else { + return Err(ProcMacroExpansionError::Panic("incorrect input".to_owned())); + }; Ok(quote! { call_site => #[macro_export] macro_rules! my_macro___ { @@ -627,45 +661,79 @@ struct AttributeInputReplaceProcMacroExpander; impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander { fn expand( &self, - _: &Subtree, - attrs: Option<&Subtree>, + _: &TopSubtree, + attrs: Option<&TopSubtree>, _: &Env, _: Span, _: Span, _: Span, _: Option, - ) -> Result, ProcMacroExpansionError> { + ) -> Result { attrs .cloned() .ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into())) } } +#[derive(Debug)] +struct Issue18840ProcMacroExpander; +impl ProcMacroExpander for Issue18840ProcMacroExpander { + fn expand( + &self, + fn_: &TopSubtree, + _: Option<&TopSubtree>, + _: &Env, + def_site: Span, + _: Span, + _: Span, + _: Option, + ) -> Result { + // Input: + // ``` + // #[issue_18840] + // fn foo() { let loop {} } + // ``` + + // The span that was created by the fixup infra. + let fixed_up_span = fn_.token_trees().flat_tokens()[5].first_span(); + let mut result = + quote! {fixed_up_span => ::core::compile_error! { "my cool compile_error!" } }; + // Make it so we won't remove the top subtree when reversing fixups. + let top_subtree_delimiter_mut = result.top_subtree_delimiter_mut(); + top_subtree_delimiter_mut.open = def_site; + top_subtree_delimiter_mut.close = def_site; + Ok(result) + } +} + #[derive(Debug)] struct MirrorProcMacroExpander; impl ProcMacroExpander for MirrorProcMacroExpander { fn expand( &self, - input: &Subtree, - _: Option<&Subtree>, + input: &TopSubtree, + _: Option<&TopSubtree>, _: &Env, _: Span, _: Span, _: Span, _: Option, - ) -> Result, ProcMacroExpansionError> { - fn traverse(input: &Subtree) -> Subtree { - let mut token_trees = vec![]; - for tt in input.token_trees.iter().rev() { - let tt = match tt { - tt::TokenTree::Leaf(leaf) => tt::TokenTree::Leaf(leaf.clone()), - tt::TokenTree::Subtree(sub) => tt::TokenTree::Subtree(traverse(sub)), - }; - token_trees.push(tt); + ) -> Result { + fn traverse(builder: &mut TopSubtreeBuilder, iter: TtIter<'_>) { + for tt in iter.collect_vec().into_iter().rev() { + match tt { + TtElement::Leaf(leaf) => builder.push(leaf.clone()), + TtElement::Subtree(subtree, subtree_iter) => { + builder.open(subtree.delimiter.kind, subtree.delimiter.open); + traverse(builder, subtree_iter); + builder.close(subtree.delimiter.close); + } + } } - Subtree { delimiter: input.delimiter, token_trees: token_trees.into_boxed_slice() } } - Ok(traverse(input)) + let mut builder = TopSubtreeBuilder::new(input.top_subtree().delimiter); + traverse(&mut builder, input.iter()); + Ok(builder.build()) } } @@ -677,31 +745,24 @@ struct ShortenProcMacroExpander; impl ProcMacroExpander for ShortenProcMacroExpander { fn expand( &self, - input: &Subtree, - _: Option<&Subtree>, + input: &TopSubtree, + _: Option<&TopSubtree>, _: &Env, _: Span, _: Span, _: Span, _: Option, - ) -> Result, ProcMacroExpansionError> { - return Ok(traverse(input)); - - fn traverse(input: &Subtree) -> Subtree { - let token_trees = input - .token_trees - .iter() - .map(|it| match it { - TokenTree::Leaf(leaf) => tt::TokenTree::Leaf(modify_leaf(leaf)), - TokenTree::Subtree(subtree) => tt::TokenTree::Subtree(traverse(subtree)), - }) - .collect(); - Subtree { delimiter: input.delimiter, token_trees } + ) -> Result { + let mut result = input.0.clone(); + for it in &mut result { + if let TokenTree::Leaf(leaf) = it { + modify_leaf(leaf) + } } + return Ok(tt::TopSubtree(result)); - fn modify_leaf(leaf: &Leaf) -> Leaf { - let mut leaf = leaf.clone(); - match &mut leaf { + fn modify_leaf(leaf: &mut Leaf) { + match leaf { Leaf::Literal(it) => { // XXX Currently replaces any literals with an empty string, but supporting // "shortening" other literals would be nice. @@ -712,7 +773,31 @@ impl ProcMacroExpander for ShortenProcMacroExpander { it.sym = Symbol::intern(&it.sym.as_str().chars().take(1).collect::()); } } - leaf } } } + +// Reads ident type within string quotes, for issue #17479. +#[derive(Debug)] +struct Issue17479ProcMacroExpander; +impl ProcMacroExpander for Issue17479ProcMacroExpander { + fn expand( + &self, + subtree: &TopSubtree, + _: Option<&TopSubtree>, + _: &Env, + _: Span, + _: Span, + _: Span, + _: Option, + ) -> Result { + let TokenTree::Leaf(Leaf::Literal(lit)) = &subtree.0[1] else { + return Err(ProcMacroExpansionError::Panic("incorrect Input".into())); + }; + let symbol = &lit.symbol; + let span = lit.span; + Ok(quote! { span => + #symbol() + }) + } +} 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 99dfabe174ee..4a2346193b49 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -17,6 +17,7 @@ //! builtin_impls: //! cell: copy, drop //! clone: sized +//! coerce_pointee: derive, sized, unsize, coerce_unsized, dispatch_from_dyn //! coerce_unsized: unsize //! concat: //! copy: clone @@ -157,6 +158,14 @@ pub mod marker { type Discriminant; } // endregion:discriminant + + // region:coerce_pointee + #[rustc_builtin_macro(CoercePointee, attributes(pointee))] + #[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)] + pub macro CoercePointee($item:item) { + /* compiler built-in */ + } + // endregion:coerce_pointee } // region:default diff --git a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs index a0603e35a09f..325b94cc33ba 100644 --- a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs +++ b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs @@ -1,6 +1,12 @@ //! Discovery of `cargo` & `rustc` executables. -use std::{env, iter, path::PathBuf}; +use std::{ + env, + ffi::OsStr, + iter, + path::{Path, PathBuf}, + process::Command, +}; use camino::{Utf8Path, Utf8PathBuf}; @@ -65,6 +71,14 @@ impl Tool { } } +pub fn command(cmd: impl AsRef, working_directory: impl AsRef) -> Command { + // we are `toolchain::command`` + #[allow(clippy::disallowed_methods)] + let mut cmd = Command::new(cmd); + cmd.current_dir(working_directory); + cmd +} + fn invoke(list: &[fn(&str) -> Option], executable: &str) -> Utf8PathBuf { list.iter().find_map(|it| it(executable)).unwrap_or_else(|| executable.into()) } @@ -102,7 +116,6 @@ fn lookup_in_path(exec: &str) -> Option { let paths = env::var_os("PATH").unwrap_or_default(); env::split_paths(&paths) .map(|path| path.join(exec)) - .map(PathBuf::from) .map(Utf8PathBuf::try_from) .filter_map(Result::ok) .find_map(probe_for_binary) diff --git a/src/tools/rust-analyzer/crates/tt/src/buffer.rs b/src/tools/rust-analyzer/crates/tt/src/buffer.rs index acb7e2d6c51a..02a722895a4b 100644 --- a/src/tools/rust-analyzer/crates/tt/src/buffer.rs +++ b/src/tools/rust-analyzer/crates/tt/src/buffer.rs @@ -1,259 +1,108 @@ //! Stateful iteration over token trees. //! //! We use this as the source of tokens for parser. -use crate::{Leaf, Subtree, TokenTree}; +use crate::{Leaf, Subtree, TokenTree, TokenTreesView}; -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -struct EntryId(usize); - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -struct EntryPtr( - /// The index of the buffer containing the entry. - EntryId, - /// The index of the entry within the buffer. - usize, -); - -/// Internal type which is used instead of `TokenTree` to represent a token tree -/// within a `TokenBuffer`. -#[derive(Debug)] -enum Entry<'t, Span> { - // Mimicking types from proc-macro. - Subtree(Option<&'t TokenTree>, &'t Subtree, EntryId), - Leaf(&'t TokenTree), - /// End entries contain a pointer to the entry from the containing - /// token tree, or [`None`] if this is the outermost level. - End(Option), -} - -/// A token tree buffer -/// The safe version of `syn` [`TokenBuffer`](https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L41) -#[derive(Debug)] -pub struct TokenBuffer<'t, Span> { - buffers: Vec]>>, -} - -trait TokenList<'a, Span> { - fn entries( - &self, - ) -> (Vec<(usize, (&'a Subtree, Option<&'a TokenTree>))>, Vec>); -} - -impl<'a, Span> TokenList<'a, Span> for &'a [TokenTree] { - fn entries( - &self, - ) -> (Vec<(usize, (&'a Subtree, Option<&'a TokenTree>))>, Vec>) - { - // Must contain everything in tokens and then the Entry::End - let start_capacity = self.len() + 1; - let mut entries = Vec::with_capacity(start_capacity); - let mut children = vec![]; - for (idx, tt) in self.iter().enumerate() { - match tt { - TokenTree::Leaf(_) => { - entries.push(Entry::Leaf(tt)); - } - TokenTree::Subtree(subtree) => { - entries.push(Entry::End(None)); - children.push((idx, (subtree, Some(tt)))); - } - } - } - (children, entries) - } -} - -impl<'a, Span> TokenList<'a, Span> for &'a Subtree { - fn entries( - &self, - ) -> (Vec<(usize, (&'a Subtree, Option<&'a TokenTree>))>, Vec>) - { - // Must contain everything in tokens and then the Entry::End - let mut entries = vec![]; - let mut children = vec![]; - entries.push(Entry::End(None)); - children.push((0usize, (*self, None))); - (children, entries) - } -} - -impl<'t, Span> TokenBuffer<'t, Span> { - pub fn from_tokens(tokens: &'t [TokenTree]) -> TokenBuffer<'t, Span> { - Self::new(tokens) - } - - pub fn from_subtree(subtree: &'t Subtree) -> TokenBuffer<'t, Span> { - Self::new(subtree) - } - - fn new>(tokens: T) -> TokenBuffer<'t, Span> { - let mut buffers = vec![]; - let idx = TokenBuffer::new_inner(tokens, &mut buffers, None); - assert_eq!(idx, 0); - TokenBuffer { buffers } - } - - fn new_inner>( - tokens: T, - buffers: &mut Vec]>>, - next: Option, - ) -> usize { - let (children, mut entries) = tokens.entries(); - - entries.push(Entry::End(next)); - let res = buffers.len(); - buffers.push(entries.into_boxed_slice()); - - for (child_idx, (subtree, tt)) in children { - let idx = TokenBuffer::new_inner( - &*subtree.token_trees, - buffers, - Some(EntryPtr(EntryId(res), child_idx + 1)), - ); - buffers[res].as_mut()[child_idx] = Entry::Subtree(tt, subtree, EntryId(idx)); - } - - res - } - - /// Creates a cursor referencing the first token in the buffer and able to - /// traverse until the end of the buffer. - pub fn begin(&self) -> Cursor<'_, Span> { - Cursor::create(self, EntryPtr(EntryId(0), 0)) - } - - fn entry(&self, ptr: &EntryPtr) -> Option<&Entry<'_, Span>> { - let id = ptr.0; - self.buffers[id.0].get(ptr.1) - } -} - -#[derive(Debug)] -pub enum TokenTreeRef<'a, Span> { - Subtree(&'a Subtree, Option<&'a TokenTree>), - Leaf(&'a Leaf, &'a TokenTree), -} - -impl TokenTreeRef<'_, Span> { - pub fn span(&self) -> Span { - match self { - TokenTreeRef::Subtree(subtree, _) => subtree.delimiter.open, - TokenTreeRef::Leaf(leaf, _) => *leaf.span(), - } - } -} - -impl TokenTreeRef<'_, Span> { - pub fn cloned(&self) -> TokenTree { - match self { - TokenTreeRef::Subtree(subtree, tt) => match tt { - Some(it) => (*it).clone(), - None => (*subtree).clone().into(), - }, - TokenTreeRef::Leaf(_, tt) => (*tt).clone(), - } - } -} - -/// A safe version of `Cursor` from `syn` crate -#[derive(Copy, Clone, Debug)] pub struct Cursor<'a, Span> { - buffer: &'a TokenBuffer<'a, Span>, - ptr: EntryPtr, + buffer: &'a [TokenTree], + index: usize, + subtrees_stack: Vec, } -impl PartialEq for Cursor<'_, Span> { - fn eq(&self, other: &Cursor<'_, Span>) -> bool { - self.ptr == other.ptr && std::ptr::eq(self.buffer, other.buffer) +impl<'a, Span: Copy> Cursor<'a, Span> { + pub fn new(buffer: &'a [TokenTree]) -> Self { + Self { buffer, index: 0, subtrees_stack: Vec::new() } } -} -impl Eq for Cursor<'_, Span> {} - -impl<'a, Span> Cursor<'a, Span> { /// Check whether it is eof - pub fn eof(self) -> bool { - matches!(self.buffer.entry(&self.ptr), None | Some(Entry::End(None))) + pub fn eof(&self) -> bool { + self.index == self.buffer.len() && self.subtrees_stack.is_empty() } - /// If the cursor is pointing at the end of a subtree, returns - /// the parent subtree - pub fn end(self) -> Option<&'a Subtree> { - match self.entry() { - Some(Entry::End(Some(ptr))) => { - let idx = ptr.1; - if let Some(Entry::Subtree(_, subtree, _)) = - self.buffer.entry(&EntryPtr(ptr.0, idx - 1)) - { - return Some(subtree); - } - None - } - _ => None, - } - } - - fn entry(&self) -> Option<&'a Entry<'a, Span>> { - self.buffer.entry(&self.ptr) - } - - /// If the cursor is pointing at a `Subtree`, returns - /// a cursor into that subtree - pub fn subtree(self) -> Option> { - match self.entry() { - Some(Entry::Subtree(_, _, entry_id)) => { - Some(Cursor::create(self.buffer, EntryPtr(*entry_id, 0))) - } - _ => None, - } - } - - /// If the cursor is pointing at a `TokenTree`, returns it - pub fn token_tree(self) -> Option> { - match self.entry() { - Some(Entry::Leaf(tt)) => match tt { - TokenTree::Leaf(leaf) => Some(TokenTreeRef::Leaf(leaf, tt)), - TokenTree::Subtree(subtree) => Some(TokenTreeRef::Subtree(subtree, Some(tt))), - }, - Some(Entry::Subtree(tt, subtree, _)) => Some(TokenTreeRef::Subtree(subtree, *tt)), - Some(Entry::End(_)) | None => None, - } - } - - fn create(buffer: &'a TokenBuffer<'_, Span>, ptr: EntryPtr) -> Cursor<'a, Span> { - Cursor { buffer, ptr } - } - - /// Bump the cursor - pub fn bump(self) -> Cursor<'a, Span> { - if let Some(Entry::End(exit)) = self.buffer.entry(&self.ptr) { - match exit { - Some(exit) => Cursor::create(self.buffer, *exit), - None => self, - } - } else { - Cursor::create(self.buffer, EntryPtr(self.ptr.0, self.ptr.1 + 1)) - } - } - - /// Bump the cursor, if it is a subtree, returns - /// a cursor into that subtree - pub fn bump_subtree(self) -> Cursor<'a, Span> { - match self.entry() { - Some(&Entry::Subtree(_, _, entry_id)) => { - Cursor::create(self.buffer, EntryPtr(entry_id, 0)) - } - Some(Entry::End(exit)) => match exit { - Some(exit) => Cursor::create(self.buffer, *exit), - None => self, - }, - _ => Cursor::create(self.buffer, EntryPtr(self.ptr.0, self.ptr.1 + 1)), - } - } - - /// Check whether it is a top level pub fn is_root(&self) -> bool { - let entry_id = self.ptr.0; - entry_id.0 == 0 + self.subtrees_stack.is_empty() + } + + fn last_subtree(&self) -> Option<(usize, &'a Subtree)> { + self.subtrees_stack.last().map(|&subtree_idx| { + let TokenTree::Subtree(subtree) = &self.buffer[subtree_idx] else { + panic!("subtree pointing to non-subtree"); + }; + (subtree_idx, subtree) + }) + } + + pub fn end(&mut self) -> &'a Subtree { + let (last_subtree_idx, last_subtree) = + self.last_subtree().expect("called `Cursor::end()` without an open subtree"); + // +1 because `Subtree.len` excludes the subtree itself. + assert_eq!( + last_subtree_idx + last_subtree.usize_len() + 1, + self.index, + "called `Cursor::end()` without finishing a subtree" + ); + self.subtrees_stack.pop(); + last_subtree + } + + /// Returns the `TokenTree` at the cursor if it is not at the end of a subtree. + pub fn token_tree(&self) -> Option<&'a TokenTree> { + if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() { + // +1 because `Subtree.len` excludes the subtree itself. + if last_subtree_idx + last_subtree.usize_len() + 1 == self.index { + return None; + } + } + self.buffer.get(self.index) + } + + /// Bump the cursor, and enters a subtree if it is on one. + pub fn bump(&mut self) { + if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() { + // +1 because `Subtree.len` excludes the subtree itself. + assert_ne!( + last_subtree_idx + last_subtree.usize_len() + 1, + self.index, + "called `Cursor::bump()` when at the end of a subtree" + ); + } + if let TokenTree::Subtree(_) = self.buffer[self.index] { + self.subtrees_stack.push(self.index); + } + self.index += 1; + } + + pub fn bump_or_end(&mut self) { + if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() { + // +1 because `Subtree.len` excludes the subtree itself. + if last_subtree_idx + last_subtree.usize_len() + 1 == self.index { + self.subtrees_stack.pop(); + return; + } + } + // +1 because `Subtree.len` excludes the subtree itself. + if let TokenTree::Subtree(_) = self.buffer[self.index] { + self.subtrees_stack.push(self.index); + } + self.index += 1; + } + + pub fn peek_two_leaves(&self) -> Option<[&'a Leaf; 2]> { + if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() { + // +1 because `Subtree.len` excludes the subtree itself. + let last_end = last_subtree_idx + last_subtree.usize_len() + 1; + if last_end == self.index || last_end == self.index + 1 { + return None; + } + } + self.buffer.get(self.index..self.index + 2).and_then(|it| match it { + [TokenTree::Leaf(a), TokenTree::Leaf(b)] => Some([a, b]), + _ => None, + }) + } + + pub fn crossed(&self) -> TokenTreesView<'a, Span> { + assert!(self.is_root()); + TokenTreesView::new(&self.buffer[..self.index]) } } diff --git a/src/tools/rust-analyzer/crates/tt/src/iter.rs b/src/tools/rust-analyzer/crates/tt/src/iter.rs index 587b903aa97a..1d88218810de 100644 --- a/src/tools/rust-analyzer/crates/tt/src/iter.rs +++ b/src/tools/rust-analyzer/crates/tt/src/iter.rs @@ -1,51 +1,64 @@ //! A "Parser" structure for token trees. We use this when parsing a declarative //! macro definition into a list of patterns and templates. +use std::fmt; + use arrayvec::ArrayVec; use intern::sym; -use crate::{Ident, Leaf, Punct, Spacing, Subtree, TokenTree}; +use crate::{Ident, Leaf, Punct, Spacing, Subtree, TokenTree, TokenTreesView}; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct TtIter<'a, S> { inner: std::slice::Iter<'a, TokenTree>, } -impl<'a, S: Copy> TtIter<'a, S> { - pub fn new(subtree: &'a Subtree) -> TtIter<'a, S> { - TtIter { inner: subtree.token_trees.iter() } +impl fmt::Debug for TtIter<'_, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TtIter").field("remaining", &self.remaining()).finish() } +} - pub fn new_iter(iter: std::slice::Iter<'a, TokenTree>) -> TtIter<'a, S> { - TtIter { inner: iter } +#[derive(Clone, Copy)] +pub struct TtIterSavepoint<'a, S>(&'a [TokenTree]); + +impl<'a, S: Copy> TtIterSavepoint<'a, S> { + pub fn remaining(self) -> TokenTreesView<'a, S> { + TokenTreesView::new(self.0) + } +} + +impl<'a, S: Copy> TtIter<'a, S> { + pub(crate) fn new(tt: &'a [TokenTree]) -> TtIter<'a, S> { + TtIter { inner: tt.iter() } } pub fn expect_char(&mut self, char: char) -> Result<(), ()> { match self.next() { - Some(&TokenTree::Leaf(Leaf::Punct(Punct { char: c, .. }))) if c == char => Ok(()), + Some(TtElement::Leaf(&Leaf::Punct(Punct { char: c, .. }))) if c == char => Ok(()), _ => Err(()), } } pub fn expect_any_char(&mut self, chars: &[char]) -> Result<(), ()> { match self.next() { - Some(TokenTree::Leaf(Leaf::Punct(Punct { char: c, .. }))) if chars.contains(c) => { + Some(TtElement::Leaf(Leaf::Punct(Punct { char: c, .. }))) if chars.contains(c) => { Ok(()) } _ => Err(()), } } - pub fn expect_subtree(&mut self) -> Result<&'a Subtree, ()> { + pub fn expect_subtree(&mut self) -> Result<(&'a Subtree, TtIter<'a, S>), ()> { match self.next() { - Some(TokenTree::Subtree(it)) => Ok(it), + Some(TtElement::Subtree(subtree, iter)) => Ok((subtree, iter)), _ => Err(()), } } pub fn expect_leaf(&mut self) -> Result<&'a Leaf, ()> { match self.next() { - Some(TokenTree::Leaf(it)) => Ok(it), + Some(TtElement::Leaf(it)) => Ok(it), _ => Err(()), } } @@ -99,7 +112,7 @@ impl<'a, S: Copy> TtIter<'a, S> { /// This method currently may return a single quotation, which is part of lifetime ident and /// conceptually not a punct in the context of mbe. Callers should handle this. pub fn expect_glued_punct(&mut self) -> Result, 3>, ()> { - let TokenTree::Leaf(Leaf::Punct(first)) = self.next().ok_or(())?.clone() else { + let TtElement::Leaf(&Leaf::Punct(first)) = self.next().ok_or(())? else { return Err(()); }; @@ -132,6 +145,7 @@ impl<'a, S: Copy> TtIter<'a, S> { } ('-' | '!' | '*' | '/' | '&' | '%' | '^' | '+' | '<' | '=' | '>' | '|', '=', _) | ('-' | '=' | '>', '>', _) + | (_, _, Some(';')) | ('<', '-', _) | (':', ':', _) | ('.', '.', _) @@ -146,28 +160,84 @@ impl<'a, S: Copy> TtIter<'a, S> { } Ok(res) } - pub fn peek_n(&self, n: usize) -> Option<&'a TokenTree> { + + /// This method won't check for subtrees, so the nth token tree may not be the nth sibling of the current tree. + fn peek_n(&self, n: usize) -> Option<&'a TokenTree> { self.inner.as_slice().get(n) } + pub fn peek(&self) -> Option> { + match self.inner.as_slice().first()? { + TokenTree::Leaf(leaf) => Some(TtElement::Leaf(leaf)), + TokenTree::Subtree(subtree) => { + let nested_iter = + TtIter { inner: self.inner.as_slice()[1..][..subtree.usize_len()].iter() }; + Some(TtElement::Subtree(subtree, nested_iter)) + } + } + } + + /// Equivalent to `peek().is_none()`, but a bit faster. + pub fn is_empty(&self) -> bool { + self.inner.len() == 0 + } + pub fn next_span(&self) -> Option { Some(self.inner.as_slice().first()?.first_span()) } - pub fn as_slice(&self) -> &'a [TokenTree] { - self.inner.as_slice() + pub fn remaining(&self) -> TokenTreesView<'a, S> { + TokenTreesView::new(self.inner.as_slice()) + } + + /// **Warning**: This advances `skip` **flat** token trees, subtrees account for children+1! + pub fn flat_advance(&mut self, skip: usize) { + self.inner = self.inner.as_slice()[skip..].iter(); + } + + pub fn savepoint(&self) -> TtIterSavepoint<'a, S> { + TtIterSavepoint(self.inner.as_slice()) + } + + pub fn from_savepoint(&self, savepoint: TtIterSavepoint<'a, S>) -> TokenTreesView<'a, S> { + let len = (self.inner.as_slice().as_ptr() as usize - savepoint.0.as_ptr() as usize) + / size_of::>(); + TokenTreesView::new(&savepoint.0[..len]) + } + + pub fn next_as_view(&mut self) -> Option> { + let savepoint = self.savepoint(); + self.next()?; + Some(self.from_savepoint(savepoint)) + } +} + +pub enum TtElement<'a, S> { + Leaf(&'a Leaf), + Subtree(&'a Subtree, TtIter<'a, S>), +} + +impl TtElement<'_, S> { + #[inline] + pub fn first_span(&self) -> S { + match self { + TtElement::Leaf(it) => *it.span(), + TtElement::Subtree(it, _) => it.delimiter.open, + } } } impl<'a, S> Iterator for TtIter<'a, S> { - type Item = &'a TokenTree; + type Item = TtElement<'a, S>; fn next(&mut self) -> Option { - self.inner.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() + match self.inner.next()? { + TokenTree::Leaf(leaf) => Some(TtElement::Leaf(leaf)), + TokenTree::Subtree(subtree) => { + let nested_iter = + TtIter { inner: self.inner.as_slice()[..subtree.usize_len()].iter() }; + self.inner = self.inner.as_slice()[subtree.usize_len()..].iter(); + Some(TtElement::Subtree(subtree, nested_iter)) + } + } } } - -impl std::iter::ExactSizeIterator for TtIter<'_, S> {} diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs index 8d915d0a51e3..7705ba876e1a 100644 --- a/src/tools/rust-analyzer/crates/tt/src/lib.rs +++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs @@ -1,6 +1,7 @@ //! `tt` crate defines a `TokenTree` data structure: this is the interface (both -//! input and output) of macros. It closely mirrors `proc_macro` crate's -//! `TokenTree`. +//! input and output) of macros. +//! +//! The `TokenTree` is semantically a tree, but for performance reasons it is stored as a flat structure. #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] @@ -14,7 +15,9 @@ pub mod iter; use std::fmt; +use buffer::Cursor; use intern::Symbol; +use iter::{TtElement, TtIter}; use stdx::{impl_from, itertools::Itertools as _}; pub use text_size::{TextRange, TextSize}; @@ -75,23 +78,6 @@ pub enum TokenTree { } impl_from!(Leaf, Subtree for TokenTree); impl TokenTree { - pub fn empty(span: S) -> Self { - Self::Subtree(Subtree { - delimiter: Delimiter::invisible_spanned(span), - token_trees: Box::new([]), - }) - } - - pub fn subtree_or_wrap(self, span: DelimSpan) -> Subtree { - match self { - TokenTree::Leaf(_) => Subtree { - delimiter: Delimiter::invisible_delim_spanned(span), - token_trees: Box::new([self]), - }, - TokenTree::Subtree(s) => s, - } - } - pub fn first_span(&self) -> S { match self { TokenTree::Leaf(l) => *l.span(), @@ -118,38 +104,422 @@ impl Leaf { } impl_from!(Literal, Punct, Ident for Leaf); -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Subtree { pub delimiter: Delimiter, - pub token_trees: Box<[TokenTree]>, + /// Number of following token trees that belong to this subtree, excluding this subtree. + pub len: u32, } -impl Subtree { - pub fn empty(span: DelimSpan) -> Self { - Subtree { delimiter: Delimiter::invisible_delim_spanned(span), token_trees: Box::new([]) } - } - - /// This is slow, and should be avoided, as it will always reallocate! - pub fn push(&mut self, subtree: TokenTree) { - let mut mutable_trees = std::mem::take(&mut self.token_trees).into_vec(); - - // Reserve exactly space for one element, to avoid `into_boxed_slice` having to reallocate again. - mutable_trees.reserve_exact(1); - mutable_trees.push(subtree); - - self.token_trees = mutable_trees.into_boxed_slice(); +impl Subtree { + pub fn usize_len(&self) -> usize { + self.len as usize } } #[derive(Clone, PartialEq, Eq, Hash)] -pub struct SubtreeBuilder { - pub delimiter: Delimiter, - pub token_trees: Vec>, +pub struct TopSubtree(pub Box<[TokenTree]>); + +impl TopSubtree { + pub fn empty(span: DelimSpan) -> Self { + Self(Box::new([TokenTree::Subtree(Subtree { + delimiter: Delimiter::invisible_delim_spanned(span), + len: 0, + })])) + } + + pub fn invisible_from_leaves(delim_span: S, leaves: [Leaf; N]) -> Self { + let mut builder = TopSubtreeBuilder::new(Delimiter::invisible_spanned(delim_span)); + builder.extend(leaves); + builder.build() + } + + pub fn from_token_trees(delimiter: Delimiter, token_trees: TokenTreesView<'_, S>) -> Self { + let mut builder = TopSubtreeBuilder::new(delimiter); + builder.extend_with_tt(token_trees); + builder.build() + } + + pub fn from_subtree(subtree: SubtreeView<'_, S>) -> Self { + Self(subtree.0.into()) + } + + pub fn view(&self) -> SubtreeView<'_, S> { + SubtreeView::new(&self.0) + } + + pub fn iter(&self) -> TtIter<'_, S> { + self.view().iter() + } + + pub fn top_subtree(&self) -> &Subtree { + self.view().top_subtree() + } + + pub fn top_subtree_delimiter_mut(&mut self) -> &mut Delimiter { + let TokenTree::Subtree(subtree) = &mut self.0[0] else { + unreachable!("the first token tree is always the top subtree"); + }; + &mut subtree.delimiter + } + + pub fn token_trees(&self) -> TokenTreesView<'_, S> { + self.view().token_trees() + } } -impl SubtreeBuilder { - pub fn build(self) -> Subtree { - Subtree { delimiter: self.delimiter, token_trees: self.token_trees.into_boxed_slice() } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TopSubtreeBuilder { + unclosed_subtree_indices: Vec, + token_trees: Vec>, + last_closed_subtree: Option, +} + +impl TopSubtreeBuilder { + pub fn new(top_delimiter: Delimiter) -> Self { + let mut result = Self { + unclosed_subtree_indices: Vec::new(), + token_trees: Vec::new(), + last_closed_subtree: None, + }; + let top_subtree = TokenTree::Subtree(Subtree { delimiter: top_delimiter, len: 0 }); + result.token_trees.push(top_subtree); + result + } + + pub fn open(&mut self, delimiter_kind: DelimiterKind, open_span: S) { + self.unclosed_subtree_indices.push(self.token_trees.len()); + self.token_trees.push(TokenTree::Subtree(Subtree { + delimiter: Delimiter { + open: open_span, + close: open_span, // Will be overwritten on close. + kind: delimiter_kind, + }, + len: 0, + })); + } + + pub fn close(&mut self, close_span: S) { + let last_unclosed_index = self + .unclosed_subtree_indices + .pop() + .expect("attempt to close a `tt::Subtree` when none is open"); + let subtree_len = (self.token_trees.len() - last_unclosed_index - 1) as u32; + let TokenTree::Subtree(subtree) = &mut self.token_trees[last_unclosed_index] else { + unreachable!("unclosed token tree is always a subtree"); + }; + subtree.len = subtree_len; + subtree.delimiter.close = close_span; + self.last_closed_subtree = Some(last_unclosed_index); + } + + /// You cannot call this consecutively, it will only work once after close. + pub fn remove_last_subtree_if_invisible(&mut self) { + let Some(last_subtree_idx) = self.last_closed_subtree else { return }; + if let TokenTree::Subtree(Subtree { + delimiter: Delimiter { kind: DelimiterKind::Invisible, .. }, + .. + }) = self.token_trees[last_subtree_idx] + { + self.token_trees.remove(last_subtree_idx); + self.last_closed_subtree = None; + } + } + + pub fn push(&mut self, leaf: Leaf) { + self.token_trees.push(TokenTree::Leaf(leaf)); + } + + pub fn extend(&mut self, leaves: impl IntoIterator>) { + self.token_trees.extend(leaves.into_iter().map(TokenTree::Leaf)); + } + + /// This does not check the token trees are valid, beware! + pub fn extend_tt_dangerous(&mut self, tt: impl IntoIterator>) { + self.token_trees.extend(tt); + } + + pub fn extend_with_tt(&mut self, tt: TokenTreesView<'_, S>) { + self.token_trees.extend(tt.0.iter().cloned()); + } + + pub fn expected_delimiter(&self) -> Option<&Delimiter> { + self.unclosed_subtree_indices.last().map(|&subtree_idx| { + let TokenTree::Subtree(subtree) = &self.token_trees[subtree_idx] else { + unreachable!("unclosed token tree is always a subtree") + }; + &subtree.delimiter + }) + } + + /// Converts unclosed subtree to a punct of their open delimiter. + // FIXME: This is incorrect to do, delimiters can never be puncts. See #18244. + pub fn flatten_unclosed_subtrees(&mut self) { + for &subtree_idx in &self.unclosed_subtree_indices { + let TokenTree::Subtree(subtree) = &self.token_trees[subtree_idx] else { + unreachable!("unclosed token tree is always a subtree") + }; + let char = match subtree.delimiter.kind { + DelimiterKind::Parenthesis => '(', + DelimiterKind::Brace => '{', + DelimiterKind::Bracket => '[', + DelimiterKind::Invisible => '$', + }; + self.token_trees[subtree_idx] = TokenTree::Leaf(Leaf::Punct(Punct { + char, + spacing: Spacing::Alone, + span: subtree.delimiter.open, + })); + } + self.unclosed_subtree_indices.clear(); + } + + /// Builds, and remove the top subtree if it has only one subtree child. + pub fn build_skip_top_subtree(mut self) -> TopSubtree { + let top_tts = TokenTreesView::new(&self.token_trees[1..]); + match top_tts.try_into_subtree() { + Some(_) => { + assert!( + self.unclosed_subtree_indices.is_empty(), + "attempt to build an unbalanced `TopSubtreeBuilder`" + ); + TopSubtree(self.token_trees.drain(1..).collect()) + } + None => self.build(), + } + } + + pub fn build(mut self) -> TopSubtree { + assert!( + self.unclosed_subtree_indices.is_empty(), + "attempt to build an unbalanced `TopSubtreeBuilder`" + ); + let total_len = self.token_trees.len() as u32; + let TokenTree::Subtree(top_subtree) = &mut self.token_trees[0] else { + unreachable!("first token tree is always a subtree"); + }; + top_subtree.len = total_len - 1; + TopSubtree(self.token_trees.into_boxed_slice()) + } + + pub fn restore_point(&self) -> SubtreeBuilderRestorePoint { + SubtreeBuilderRestorePoint { + unclosed_subtree_indices_len: self.unclosed_subtree_indices.len(), + token_trees_len: self.token_trees.len(), + last_closed_subtree: self.last_closed_subtree, + } + } + + pub fn restore(&mut self, restore_point: SubtreeBuilderRestorePoint) { + self.unclosed_subtree_indices.truncate(restore_point.unclosed_subtree_indices_len); + self.token_trees.truncate(restore_point.token_trees_len); + self.last_closed_subtree = restore_point.last_closed_subtree; + } +} + +#[derive(Clone, Copy)] +pub struct SubtreeBuilderRestorePoint { + unclosed_subtree_indices_len: usize, + token_trees_len: usize, + last_closed_subtree: Option, +} + +#[derive(Clone, Copy)] +pub struct TokenTreesView<'a, S>(&'a [TokenTree]); + +impl<'a, S: Copy> TokenTreesView<'a, S> { + pub fn new(tts: &'a [TokenTree]) -> Self { + if cfg!(debug_assertions) { + tts.iter().enumerate().for_each(|(idx, tt)| { + if let TokenTree::Subtree(tt) = &tt { + // `<` and not `<=` because `Subtree.len` does not include the subtree node itself. + debug_assert!( + idx + tt.usize_len() < tts.len(), + "`TokenTreeView::new()` was given a cut-in-half list" + ); + } + }); + } + Self(tts) + } + + pub fn iter(&self) -> TtIter<'a, S> { + TtIter::new(self.0) + } + + pub fn cursor(&self) -> Cursor<'a, S> { + Cursor::new(self.0) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn try_into_subtree(self) -> Option> { + if let Some(TokenTree::Subtree(subtree)) = self.0.first() { + if subtree.usize_len() == (self.0.len() - 1) { + return Some(SubtreeView::new(self.0)); + } + } + None + } + + pub fn strip_invisible(self) -> TokenTreesView<'a, S> { + self.try_into_subtree().map(|subtree| subtree.strip_invisible()).unwrap_or(self) + } + + /// This returns a **flat** structure of tokens (subtrees will be represented by a single node + /// preceding their children), so it isn't suited for most use cases, only for matching leaves + /// at the beginning/end with no subtrees before them. If you need a structured pass, use [`TtIter`]. + pub fn flat_tokens(&self) -> &'a [TokenTree] { + self.0 + } + + pub fn split( + self, + mut split_fn: impl FnMut(TtElement<'a, S>) -> bool, + ) -> impl Iterator> { + let mut subtree_iter = self.iter(); + let mut need_to_yield_even_if_empty = true; + let result = std::iter::from_fn(move || { + if subtree_iter.is_empty() && !need_to_yield_even_if_empty { + return None; + }; + + need_to_yield_even_if_empty = false; + let savepoint = subtree_iter.savepoint(); + let mut result = subtree_iter.from_savepoint(savepoint); + while let Some(tt) = subtree_iter.next() { + if split_fn(tt) { + need_to_yield_even_if_empty = true; + break; + } + result = subtree_iter.from_savepoint(savepoint); + } + Some(result) + }); + result + } +} + +impl fmt::Debug for TokenTreesView<'_, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut iter = self.iter(); + while let Some(tt) = iter.next() { + print_debug_token(f, 0, tt)?; + if !iter.is_empty() { + writeln!(f)?; + } + } + Ok(()) + } +} + +impl fmt::Display for TokenTreesView<'_, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + return token_trees_display(f, self.iter()); + + fn subtree_display( + subtree: &Subtree, + f: &mut fmt::Formatter<'_>, + iter: TtIter<'_, S>, + ) -> fmt::Result { + let (l, r) = match subtree.delimiter.kind { + DelimiterKind::Parenthesis => ("(", ")"), + DelimiterKind::Brace => ("{", "}"), + DelimiterKind::Bracket => ("[", "]"), + DelimiterKind::Invisible => ("", ""), + }; + f.write_str(l)?; + token_trees_display(f, iter)?; + f.write_str(r)?; + Ok(()) + } + + fn token_trees_display(f: &mut fmt::Formatter<'_>, iter: TtIter<'_, S>) -> fmt::Result { + let mut needs_space = false; + for child in iter { + if needs_space { + f.write_str(" ")?; + } + needs_space = true; + + match child { + TtElement::Leaf(Leaf::Punct(p)) => { + needs_space = p.spacing == Spacing::Alone; + fmt::Display::fmt(p, f)?; + } + TtElement::Leaf(leaf) => fmt::Display::fmt(leaf, f)?, + TtElement::Subtree(subtree, subtree_iter) => { + subtree_display(subtree, f, subtree_iter)? + } + } + } + Ok(()) + } + } +} + +#[derive(Clone, Copy)] +// Invariant: always starts with `Subtree` that covers the entire thing. +pub struct SubtreeView<'a, S>(&'a [TokenTree]); + +impl<'a, S: Copy> SubtreeView<'a, S> { + pub fn new(tts: &'a [TokenTree]) -> Self { + if cfg!(debug_assertions) { + let TokenTree::Subtree(subtree) = &tts[0] else { + panic!("first token tree must be a subtree in `SubtreeView`"); + }; + assert_eq!( + subtree.usize_len(), + tts.len() - 1, + "subtree must cover the entire `SubtreeView`" + ); + } + Self(tts) + } + + pub fn as_token_trees(self) -> TokenTreesView<'a, S> { + TokenTreesView::new(self.0) + } + + pub fn iter(&self) -> TtIter<'a, S> { + TtIter::new(&self.0[1..]) + } + + pub fn top_subtree(&self) -> &'a Subtree { + let TokenTree::Subtree(subtree) = &self.0[0] else { + unreachable!("the first token tree is always the top subtree"); + }; + subtree + } + + pub fn strip_invisible(&self) -> TokenTreesView<'a, S> { + if self.top_subtree().delimiter.kind == DelimiterKind::Invisible { + TokenTreesView::new(&self.0[1..]) + } else { + TokenTreesView::new(self.0) + } + } + + pub fn token_trees(&self) -> TokenTreesView<'a, S> { + TokenTreesView::new(&self.0[1..]) + } +} + +impl fmt::Debug for SubtreeView<'_, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&TokenTreesView(self.0), f) + } +} + +impl fmt::Display for SubtreeView<'_, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&TokenTreesView(self.0), f) } } @@ -348,6 +718,7 @@ fn print_debug_subtree( f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usize, + iter: TtIter<'_, S>, ) -> fmt::Result { let align = " ".repeat(level); @@ -363,14 +734,9 @@ fn print_debug_subtree( fmt::Debug::fmt(&open, f)?; write!(f, " ")?; fmt::Debug::fmt(&close, f)?; - if !subtree.token_trees.is_empty() { + for child in iter { writeln!(f)?; - for (idx, child) in subtree.token_trees.iter().enumerate() { - print_debug_token(f, child, level + 1)?; - if idx != subtree.token_trees.len() - 1 { - writeln!(f)?; - } - } + print_debug_token(f, level + 1, child)?; } Ok(()) @@ -378,13 +744,13 @@ fn print_debug_subtree( fn print_debug_token( f: &mut fmt::Formatter<'_>, - tkn: &TokenTree, level: usize, + tt: TtElement<'_, S>, ) -> fmt::Result { let align = " ".repeat(level); - match tkn { - TokenTree::Leaf(leaf) => match leaf { + match tt { + TtElement::Leaf(leaf) => match leaf { Leaf::Literal(lit) => { write!( f, @@ -417,54 +783,23 @@ fn print_debug_token( )?; } }, - TokenTree::Subtree(subtree) => { - print_debug_subtree(f, subtree, level)?; + TtElement::Subtree(subtree, subtree_iter) => { + print_debug_subtree(f, subtree, level, subtree_iter)?; } } Ok(()) } -impl fmt::Debug for Subtree { +impl fmt::Debug for TopSubtree { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - print_debug_subtree(f, self, 0) + fmt::Debug::fmt(&self.view(), f) } } -impl fmt::Display for TokenTree { +impl fmt::Display for TopSubtree { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TokenTree::Leaf(it) => fmt::Display::fmt(it, f), - TokenTree::Subtree(it) => fmt::Display::fmt(it, f), - } - } -} - -impl fmt::Display for Subtree { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (l, r) = match self.delimiter.kind { - DelimiterKind::Parenthesis => ("(", ")"), - DelimiterKind::Brace => ("{", "}"), - DelimiterKind::Bracket => ("[", "]"), - DelimiterKind::Invisible => ("", ""), - }; - f.write_str(l)?; - let mut needs_space = false; - for tt in self.token_trees.iter() { - if needs_space { - f.write_str(" ")?; - } - needs_space = true; - match tt { - TokenTree::Leaf(Leaf::Punct(p)) => { - needs_space = p.spacing == Spacing::Alone; - fmt::Display::fmt(p, f)?; - } - tt => fmt::Display::fmt(tt, f)?, - } - } - f.write_str(r)?; - Ok(()) + fmt::Display::fmt(&self.view(), f) } } @@ -538,34 +873,45 @@ impl fmt::Display for Punct { impl Subtree { /// Count the number of tokens recursively pub fn count(&self) -> usize { - let children_count = self - .token_trees - .iter() - .map(|c| match c { - TokenTree::Subtree(c) => c.count(), - TokenTree::Leaf(_) => 0, - }) - .sum::(); - - self.token_trees.len() + children_count + self.usize_len() } } -impl Subtree { +impl TopSubtree { /// A simple line string used for debugging - pub fn as_debug_string(&self) -> String { - let delim = match self.delimiter.kind { - DelimiterKind::Brace => ("{", "}"), - DelimiterKind::Bracket => ("[", "]"), - DelimiterKind::Parenthesis => ("(", ")"), - DelimiterKind::Invisible => ("$", "$"), - }; + pub fn subtree_as_debug_string(&self, subtree_idx: usize) -> String { + fn debug_subtree( + output: &mut String, + subtree: &Subtree, + iter: &mut std::slice::Iter<'_, TokenTree>, + ) { + let delim = match subtree.delimiter.kind { + DelimiterKind::Brace => ("{", "}"), + DelimiterKind::Bracket => ("[", "]"), + DelimiterKind::Parenthesis => ("(", ")"), + DelimiterKind::Invisible => ("$", "$"), + }; - let mut res = String::new(); - res.push_str(delim.0); - let mut last = None; - for child in self.token_trees.iter() { - let s = match child { + output.push_str(delim.0); + let mut last = None; + let mut idx = 0; + while idx < subtree.len { + let child = iter.next().unwrap(); + debug_token_tree(output, child, last, iter); + last = Some(child); + idx += 1; + } + + output.push_str(delim.1); + } + + fn debug_token_tree( + output: &mut String, + tt: &TokenTree, + last: Option<&TokenTree>, + iter: &mut std::slice::Iter<'_, TokenTree>, + ) { + match tt { TokenTree::Leaf(it) => { let s = match it { Leaf::Literal(it) => it.symbol.to_string(), @@ -574,31 +920,37 @@ impl Subtree { }; match (it, last) { (Leaf::Ident(_), Some(&TokenTree::Leaf(Leaf::Ident(_)))) => { - " ".to_owned() + &s + output.push(' '); + output.push_str(&s); } (Leaf::Punct(_), Some(TokenTree::Leaf(Leaf::Punct(punct)))) => { if punct.spacing == Spacing::Alone { - " ".to_owned() + &s + output.push(' '); + output.push_str(&s); } else { - s + output.push_str(&s); } } - _ => s, + _ => output.push_str(&s), } } - TokenTree::Subtree(it) => it.as_debug_string(), - }; - res.push_str(&s); - last = Some(child); + TokenTree::Subtree(it) => debug_subtree(output, it, iter), + } } - res.push_str(delim.1); + let mut res = String::new(); + debug_token_tree( + &mut res, + &self.0[subtree_idx], + None, + &mut self.0[subtree_idx + 1..].iter(), + ); res } } -pub fn pretty(tkns: &[TokenTree]) -> String { - fn tokentree_to_text(tkn: &TokenTree) -> String { +pub fn pretty(mut tkns: &[TokenTree]) -> String { + fn tokentree_to_text(tkn: &TokenTree, tkns: &mut &[TokenTree]) -> String { match tkn { TokenTree::Leaf(Leaf::Ident(ident)) => { format!("{}{}", ident.is_raw.as_str(), ident.sym) @@ -606,7 +958,9 @@ pub fn pretty(tkns: &[TokenTree]) -> String { TokenTree::Leaf(Leaf::Literal(literal)) => format!("{literal}"), TokenTree::Leaf(Leaf::Punct(punct)) => format!("{}", punct.char), TokenTree::Subtree(subtree) => { - let content = pretty(&subtree.token_trees); + let (subtree_content, rest) = tkns.split_at(subtree.usize_len()); + let content = pretty(subtree_content); + *tkns = rest; let (open, close) = match subtree.delimiter.kind { DelimiterKind::Brace => ("{", "}"), DelimiterKind::Bracket => ("[", "]"), @@ -618,16 +972,18 @@ pub fn pretty(tkns: &[TokenTree]) -> String { } } - tkns.iter() - .fold((String::new(), true), |(last, last_to_joint), tkn| { - let s = [last, tokentree_to_text(tkn)].join(if last_to_joint { "" } else { " " }); - let mut is_joint = false; - if let TokenTree::Leaf(Leaf::Punct(punct)) = tkn { - if punct.spacing == Spacing::Joint { - is_joint = true; - } + let mut last = String::new(); + let mut last_to_joint = true; + + while let Some((tkn, rest)) = tkns.split_first() { + tkns = rest; + last = [last, tokentree_to_text(tkn, &mut tkns)].join(if last_to_joint { "" } else { " " }); + last_to_joint = false; + if let TokenTree::Leaf(Leaf::Punct(punct)) = tkn { + if punct.spacing == Spacing::Joint { + last_to_joint = true; } - (s, is_joint) - }) - .0 + } + } + last } diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index 0e37611a5493..21ac3a5a2693 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ $DIR/associated-types-for-unimpl-trait.rs:11:81 + --> $DIR/associated-types-for-unimpl-trait.rs:11:41 | LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Sized {} - | ^^ the trait `Get` is not implemented for `Self` + | ^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider further restricting `Self` | LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Sized, Self: Get {} diff --git a/tests/ui/associated-types/associated-types-no-suitable-bound.stderr b/tests/ui/associated-types/associated-types-no-suitable-bound.stderr index 4f951ee4b4e6..b0598b3810ca 100644 --- a/tests/ui/associated-types/associated-types-no-suitable-bound.stderr +++ b/tests/ui/associated-types/associated-types-no-suitable-bound.stderr @@ -10,11 +10,12 @@ LL | fn uhoh(foo: ::Value) {} | +++++ error[E0277]: the trait bound `T: Get` is not satisfied - --> $DIR/associated-types-no-suitable-bound.rs:11:40 + --> $DIR/associated-types-no-suitable-bound.rs:11:21 | LL | fn uhoh(foo: ::Value) {} - | ^^ the trait `Get` is not implemented for `T` + | ^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `T` | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider restricting type parameter `T` with trait `Get` | LL | fn uhoh(foo: ::Value) {} diff --git a/tests/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr b/tests/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr index c5dcfc00925d..fce8c7ed384d 100644 --- a/tests/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr +++ b/tests/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr @@ -10,11 +10,12 @@ LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Ge | +++++++++++++++ error[E0277]: the trait bound `Self: Get` is not satisfied - --> $DIR/associated-types-no-suitable-supertrait-2.rs:17:62 + --> $DIR/associated-types-no-suitable-supertrait-2.rs:17:40 | LL | fn uhoh(&self, foo: U, bar: ::Value) {} - | ^^ the trait `Get` is not implemented for `Self` + | ^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider further restricting `Self` | LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Get {} diff --git a/tests/ui/associated-types/associated-types-no-suitable-supertrait.stderr b/tests/ui/associated-types/associated-types-no-suitable-supertrait.stderr index 46cebda078e4..b7d528025bd1 100644 --- a/tests/ui/associated-types/associated-types-no-suitable-supertrait.stderr +++ b/tests/ui/associated-types/associated-types-no-suitable-supertrait.stderr @@ -34,27 +34,29 @@ LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Ge | +++++++++++++++ error[E0277]: the trait bound `Self: Get` is not satisfied - --> $DIR/associated-types-no-suitable-supertrait.rs:17:62 + --> $DIR/associated-types-no-suitable-supertrait.rs:17:40 | LL | fn uhoh(&self, foo: U, bar: ::Value) {} - | ^^ the trait `Get` is not implemented for `Self` + | ^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider further restricting `Self` | LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Get {} | +++++++++++++++ error[E0277]: the trait bound `(T, U): Get` is not satisfied - --> $DIR/associated-types-no-suitable-supertrait.rs:23:64 + --> $DIR/associated-types-no-suitable-supertrait.rs:23:40 | LL | fn uhoh(&self, foo: U, bar: <(T, U) as Get>::Value) {} - | ^^ the trait `Get` is not implemented for `(T, U)` + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `(T, U)` | help: this trait has no implementations, consider adding one --> $DIR/associated-types-no-suitable-supertrait.rs:12:1 | LL | trait Get { | ^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 5 previous errors diff --git a/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.stderr b/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.stderr index e12d42e5ed0c..42d83fca6ca8 100644 --- a/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.stderr +++ b/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.stderr @@ -7,9 +7,9 @@ LL | fn bar<'a, 'b, I : for<'x> Foo<&'x isize>>( | lifetime `'a` defined here ... LL | let z: I::A = if cond { x } else { y }; - | ^ assignment requires that `'a` must outlive `'b` + | ^ assignment requires that `'b` must outlive `'a` | - = help: consider adding the following bound: `'a: 'b` + = help: consider adding the following bound: `'b: 'a` error: lifetime may not live long enough --> $DIR/associated-types-project-from-hrtb-in-fn-body.rs:22:40 @@ -20,9 +20,9 @@ LL | fn bar<'a, 'b, I : for<'x> Foo<&'x isize>>( | lifetime `'a` defined here ... LL | let z: I::A = if cond { x } else { y }; - | ^ assignment requires that `'b` must outlive `'a` + | ^ assignment requires that `'a` must outlive `'b` | - = help: consider adding the following bound: `'b: 'a` + = help: consider adding the following bound: `'a: 'b` help: `'a` and `'b` must be the same: replace one with the other diff --git a/tests/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr b/tests/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr index 77841780f621..3ef6b85c407a 100644 --- a/tests/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr +++ b/tests/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr @@ -7,9 +7,9 @@ LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { | lifetime `'a` defined here LL | let f = foo; // <-- No consistent type can be inferred for `f` here. LL | let a = bar(f, x); - | ^^^^^^^^^ argument requires that `'a` must outlive `'b` + | ^^^^^^^^^ argument requires that `'b` must outlive `'a` | - = help: consider adding the following bound: `'a: 'b` + = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of the type `Type<'_>`, which makes the generic argument `'_` invariant = note: the struct `Type<'a>` is invariant over the parameter `'a` = help: see for more information about variance @@ -23,9 +23,9 @@ LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { | lifetime `'a` defined here ... LL | let b = bar(f, y); - | ^^^^^^^^^ argument requires that `'b` must outlive `'a` + | ^^^^^^^^^ argument requires that `'a` must outlive `'b` | - = help: consider adding the following bound: `'b: 'a` + = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of the type `Type<'_>`, which makes the generic argument `'_` invariant = note: the struct `Type<'a>` is invariant over the parameter `'a` = help: see for more information about variance diff --git a/tests/ui/associated-types/invalid-ctor.fixed b/tests/ui/associated-types/invalid-ctor.fixed new file mode 100644 index 000000000000..eba3820de0c1 --- /dev/null +++ b/tests/ui/associated-types/invalid-ctor.fixed @@ -0,0 +1,22 @@ +//@ run-rustfix + +#![allow(unused)] + +struct Constructor(i32); + +trait Trait { + type Out; + + fn mk() -> Self::Out; +} + +impl Trait for () { + type Out = Constructor; + + fn mk() -> Self::Out { + Constructor(1) + //~^ ERROR no associated item named `Out` found for unit type `()` + } +} + +fn main() {} diff --git a/tests/ui/associated-types/invalid-ctor.rs b/tests/ui/associated-types/invalid-ctor.rs new file mode 100644 index 000000000000..73335c065c2a --- /dev/null +++ b/tests/ui/associated-types/invalid-ctor.rs @@ -0,0 +1,22 @@ +//@ run-rustfix + +#![allow(unused)] + +struct Constructor(i32); + +trait Trait { + type Out; + + fn mk() -> Self::Out; +} + +impl Trait for () { + type Out = Constructor; + + fn mk() -> Self::Out { + Self::Out(1) + //~^ ERROR no associated item named `Out` found for unit type `()` + } +} + +fn main() {} diff --git a/tests/ui/associated-types/invalid-ctor.stderr b/tests/ui/associated-types/invalid-ctor.stderr new file mode 100644 index 000000000000..b545c95a7681 --- /dev/null +++ b/tests/ui/associated-types/invalid-ctor.stderr @@ -0,0 +1,14 @@ +error[E0599]: no associated item named `Out` found for unit type `()` in the current scope + --> $DIR/invalid-ctor.rs:17:15 + | +LL | Self::Out(1) + | ^^^ associated item not found in `()` + | +help: to construct a value of type `Constructor`, use the explicit path + | +LL | Constructor(1) + | ~~~~~~~~~~~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/associated-types/issue-59324.stderr b/tests/ui/associated-types/issue-59324.stderr index 805c3e60bb6a..2abe337b69ae 100644 --- a/tests/ui/associated-types/issue-59324.stderr +++ b/tests/ui/associated-types/issue-59324.stderr @@ -65,16 +65,17 @@ LL | pub trait ThriftService: | +++++ error[E0277]: the trait bound `(): Foo` is not satisfied - --> $DIR/issue-59324.rs:23:52 + --> $DIR/issue-59324.rs:23:29 | LL | fn with_factory(factory: dyn ThriftService<()>) {} - | ^^ the trait `Foo` is not implemented for `()` + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()` | help: this trait has no implementations, consider adding one --> $DIR/issue-59324.rs:3:1 | LL | pub trait Foo: NotFoo { | ^^^^^^^^^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the size for values of type `(dyn ThriftService<(), AssocType = _> + 'static)` cannot be known at compilation time --> $DIR/issue-59324.rs:23:29 diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr index be39dbf313bf..329cec6dad3a 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr @@ -7,7 +7,7 @@ LL | let c = async || { println!("{}", *x); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough LL | outlives::<'a>(c()); LL | outlives::<'a>(call_once(c)); - | ------------ argument requires that `x` is borrowed for `'a` + | ---------------------------- argument requires that `x` is borrowed for `'a` ... LL | } | - `x` dropped here while still borrowed @@ -21,10 +21,10 @@ LL | fn simple<'a>(x: &'a i32) { LL | let c = async move || { println!("{}", *x); }; | - binding `c` declared here LL | outlives::<'a>(c()); - | ^-- - | | - | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` + | ---------------^--- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed @@ -38,7 +38,7 @@ LL | let c = async || { println!("{}", *x.0); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough LL | outlives::<'a>(c()); LL | outlives::<'a>(call_once(c)); - | ------------ argument requires that `x` is borrowed for `'a` + | ---------------------------- argument requires that `x` is borrowed for `'a` ... LL | } | - `x` dropped here while still borrowed @@ -52,7 +52,7 @@ LL | let c = async || { println!("{}", *x.0); }; | ---------------------------------- borrow of `x` occurs here LL | outlives::<'a>(c()); LL | outlives::<'a>(call_once(c)); - | ------------ argument requires that `x` is borrowed for `'a` + | ---------------------------- argument requires that `x` is borrowed for `'a` LL | LL | let c = async move || { println!("{}", *x.0); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `x` occurs here @@ -66,10 +66,10 @@ LL | fn through_field<'a>(x: S<'a>) { LL | let c = async move || { println!("{}", *x.0); }; | - binding `c` declared here LL | outlives::<'a>(c()); - | ^-- - | | - | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` + | ---------------^--- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed @@ -83,10 +83,10 @@ LL | fn through_field<'a>(x: S<'a>) { LL | let c = async move || { println!("{}", *x.0); }; | - binding `c` declared here LL | outlives::<'a>(c()); - | --- - | | - | borrow of `c` occurs here - | argument requires that `c` is borrowed for `'a` + | ------------------- + | | | + | | borrow of `c` occurs here + | argument requires that `c` is borrowed for `'a` LL | outlives::<'a>(call_once(c)); | ^ move out of `c` occurs here @@ -99,18 +99,18 @@ LL | let c = async || { println!("{}", *x.0); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough LL | outlives::<'a>(c()); LL | outlives::<'a>(call_once(c)); - | ------------ argument requires that `x` is borrowed for `'a` + | ---------------------------- argument requires that `x` is borrowed for `'a` LL | } | - `x` dropped here while still borrowed error[E0621]: explicit lifetime required in the type of `x` - --> $DIR/without-precise-captures-we-are-powerless.rs:38:20 + --> $DIR/without-precise-captures-we-are-powerless.rs:38:5 | LL | fn through_field_and_ref<'a>(x: &S<'a>) { | ------ help: add explicit lifetime `'a` to the type of `x`: `&'a S<'a>` ... LL | outlives::<'a>(call_once(c)); - | ^^^^^^^^^^^^ lifetime `'a` required + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'a` required error[E0597]: `c` does not live long enough --> $DIR/without-precise-captures-we-are-powerless.rs:43:20 @@ -120,22 +120,22 @@ LL | fn through_field_and_ref_move<'a>(x: &S<'a>) { LL | let c = async move || { println!("{}", *x.0); }; | - binding `c` declared here LL | outlives::<'a>(c()); - | ^-- - | | - | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` + | ---------------^--- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed error[E0621]: explicit lifetime required in the type of `x` - --> $DIR/without-precise-captures-we-are-powerless.rs:44:20 + --> $DIR/without-precise-captures-we-are-powerless.rs:44:5 | LL | fn through_field_and_ref_move<'a>(x: &S<'a>) { | ------ help: add explicit lifetime `'a` to the type of `x`: `&'a S<'a>` ... LL | outlives::<'a>(call_once(c)); - | ^^^^^^^^^^^^ lifetime `'a` required + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'a` required error: aborting due to 10 previous errors diff --git a/tests/ui/async-await/issues/issue-63388-1.rs b/tests/ui/async-await/issues/issue-63388-1.rs index a6f499ba94e2..acfc64baff97 100644 --- a/tests/ui/async-await/issues/issue-63388-1.rs +++ b/tests/ui/async-await/issues/issue-63388-1.rs @@ -11,8 +11,8 @@ impl Xyz { &'a self, foo: &dyn Foo ) -> &dyn Foo //~ WARNING elided lifetime has a name { - //~^ ERROR explicit lifetime required in the type of `foo` [E0621] foo + //~^ ERROR explicit lifetime required in the type of `foo` [E0621] } } diff --git a/tests/ui/async-await/issues/issue-63388-1.stderr b/tests/ui/async-await/issues/issue-63388-1.stderr index ef74bfe32375..579caa45bc94 100644 --- a/tests/ui/async-await/issues/issue-63388-1.stderr +++ b/tests/ui/async-await/issues/issue-63388-1.stderr @@ -10,16 +10,13 @@ LL | ) -> &dyn Foo = note: `#[warn(elided_named_lifetimes)]` on by default error[E0621]: explicit lifetime required in the type of `foo` - --> $DIR/issue-63388-1.rs:13:5 + --> $DIR/issue-63388-1.rs:14:9 | -LL | &'a self, foo: &dyn Foo - | -------- help: add explicit lifetime `'a` to the type of `foo`: `&'a (dyn Foo + 'a)` -LL | ) -> &dyn Foo -LL | / { -LL | | -LL | | foo -LL | | } - | |_____^ lifetime `'a` required +LL | &'a self, foo: &dyn Foo + | -------- help: add explicit lifetime `'a` to the type of `foo`: `&'a (dyn Foo + 'a)` +... +LL | foo + | ^^^ lifetime `'a` required error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/async-await/issues/issue-63388-2.rs b/tests/ui/async-await/issues/issue-63388-2.rs index 85718f411215..8bb5cfa4a594 100644 --- a/tests/ui/async-await/issues/issue-63388-2.rs +++ b/tests/ui/async-await/issues/issue-63388-2.rs @@ -11,8 +11,8 @@ impl Xyz { foo: &dyn Foo, bar: &'a dyn Foo ) -> &dyn Foo //~ ERROR missing lifetime specifier { - //~^ ERROR explicit lifetime required in the type of `foo` [E0621] foo + //~^ ERROR explicit lifetime required in the type of `foo` [E0621] } } diff --git a/tests/ui/async-await/issues/issue-63388-2.stderr b/tests/ui/async-await/issues/issue-63388-2.stderr index e515f227c7ef..7e3c0a1227de 100644 --- a/tests/ui/async-await/issues/issue-63388-2.stderr +++ b/tests/ui/async-await/issues/issue-63388-2.stderr @@ -13,16 +13,13 @@ LL | ) -> &'a dyn Foo | ++ error[E0621]: explicit lifetime required in the type of `foo` - --> $DIR/issue-63388-2.rs:13:5 + --> $DIR/issue-63388-2.rs:14:9 | -LL | foo: &dyn Foo, bar: &'a dyn Foo - | -------- help: add explicit lifetime `'a` to the type of `foo`: `&'a (dyn Foo + 'a)` -LL | ) -> &dyn Foo -LL | / { -LL | | -LL | | foo -LL | | } - | |_____^ lifetime `'a` required +LL | foo: &dyn Foo, bar: &'a dyn Foo + | -------- help: add explicit lifetime `'a` to the type of `foo`: `&'a (dyn Foo + 'a)` +... +LL | foo + | ^^^ lifetime `'a` required error: aborting due to 2 previous errors diff --git a/tests/ui/auto-traits/issue-83857-ub.stderr b/tests/ui/auto-traits/issue-83857-ub.stderr index 7c437b7e6c84..3536450c75b1 100644 --- a/tests/ui/auto-traits/issue-83857-ub.stderr +++ b/tests/ui/auto-traits/issue-83857-ub.stderr @@ -18,16 +18,10 @@ LL | fn generic(v: Foo, f: fn( as WithAssoc>::Output) -> i | +++++++++++++++++++++ error[E0277]: `Foo` cannot be sent between threads safely - --> $DIR/issue-83857-ub.rs:21:80 + --> $DIR/issue-83857-ub.rs:21:35 | -LL | fn generic(v: Foo, f: fn( as WithAssoc>::Output) -> i32) { - | ________________________________________________________________________________^ -LL | | -LL | | -LL | | f(foo(v)); -LL | | -LL | | } - | |_^ `Foo` cannot be sent between threads safely +LL | fn generic(v: Foo, f: fn( as WithAssoc>::Output) -> i32) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` cannot be sent between threads safely | = help: the trait `Send` is not implemented for `Foo` note: required for `Foo` to implement `WithAssoc` diff --git a/tests/ui/borrowck/fn-item-check-type-params.stderr b/tests/ui/borrowck/fn-item-check-type-params.stderr index 3a29edc55c54..aafb7e66ef55 100644 --- a/tests/ui/borrowck/fn-item-check-type-params.stderr +++ b/tests/ui/borrowck/fn-item-check-type-params.stderr @@ -12,12 +12,12 @@ LL | extend_lt(val); | argument requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/fn-item-check-type-params.rs:39:12 + --> $DIR/fn-item-check-type-params.rs:39:31 | LL | pub fn test_coercion<'a>() { | -- lifetime `'a` defined here LL | let _: fn(&'a str) -> _ = extend_lt; - | ^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^ coercion requires that `'a` must outlive `'static` error[E0716]: temporary value dropped while borrowed --> $DIR/fn-item-check-type-params.rs:48:11 diff --git a/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr b/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr index 13c768dcbf63..4ec4d2138db6 100644 --- a/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr +++ b/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr @@ -15,10 +15,10 @@ LL | async { LL | let not_static = 0; | ---------- binding `not_static` declared here LL | force_send(async_load(¬_static)); - | -----------^^^^^^^^^^^- - | | | - | | borrowed value does not live long enough - | argument requires that `not_static` is borrowed for `'1` + | ----------------------^^^^^^^^^^^-- + | | | + | | borrowed value does not live long enough + | argument requires that `not_static` is borrowed for `'1` ... LL | } | - `not_static` dropped here while still borrowed diff --git a/tests/ui/borrowck/issue-7573.rs b/tests/ui/borrowck/issue-7573.rs index 7c07411533ff..f94cd75ab6e2 100644 --- a/tests/ui/borrowck/issue-7573.rs +++ b/tests/ui/borrowck/issue-7573.rs @@ -17,6 +17,8 @@ pub fn remove_package_from_database() { lines_to_use.push(installed_id); //~^ ERROR borrowed data escapes outside of closure //~| NOTE `installed_id` escapes the closure body here + //~| NOTE requirement occurs because of a mutable reference to `Vec<&CrateId>` + //~| NOTE mutable references are invariant over their type parameter }; list_database(push_id); diff --git a/tests/ui/borrowck/issue-7573.stderr b/tests/ui/borrowck/issue-7573.stderr index 07a67474c831..d0121f4ec174 100644 --- a/tests/ui/borrowck/issue-7573.stderr +++ b/tests/ui/borrowck/issue-7573.stderr @@ -9,6 +9,10 @@ LL | let push_id = |installed_id: &CrateId| { LL | LL | lines_to_use.push(installed_id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `installed_id` escapes the closure body here + | + = note: requirement occurs because of a mutable reference to `Vec<&CrateId>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr b/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr index 89b15a7a659d..4d1d7ffd2935 100644 --- a/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr +++ b/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr @@ -71,14 +71,13 @@ error[E0502]: cannot borrow `*reg` as mutable because it is also borrowed as imm LL | fn register_plugins<'a>(mk_reg: impl Fn() -> &'a mut Registry<'a>) { | -- lifetime `'a` defined here ... +LL | let reg = mk_reg(); + | -------- assignment requires that `reg.sess_mut` is borrowed for `'a` LL | reg.register_univ(Box::new(CapturePass::new(®.sess_mut))); - | ^^^^^^^^^^^^^^^^^^-----------------------------------------^ - | | | | - | | | immutable borrow occurs here - | | coercion requires that `reg.sess_mut` is borrowed for `'a` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------------^^^ + | | | + | | immutable borrow occurs here | mutable borrow occurs here - | - = note: due to object lifetime defaults, `Box LateLintPass<'b>>` actually means `Box<(dyn for<'b> LateLintPass<'b> + 'static)>` error[E0502]: cannot borrow `*reg` as mutable because it is also borrowed as immutable --> $DIR/two-phase-surprise-no-conflict.rs:144:5 @@ -115,14 +114,13 @@ error[E0499]: cannot borrow `*reg` as mutable more than once at a time LL | fn register_plugins<'a>(mk_reg: impl Fn() -> &'a mut Registry<'a>) { | -- lifetime `'a` defined here ... +LL | let reg = mk_reg(); + | -------- assignment requires that `reg.sess_mut` is borrowed for `'a` LL | reg.register_univ(Box::new(CapturePass::new_mut(&mut reg.sess_mut))); - | ^^^^^^^^^^^^^^^^^^-------------------------------------------------^ - | | | | - | | | first mutable borrow occurs here - | | coercion requires that `reg.sess_mut` is borrowed for `'a` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------------^^^ + | | | + | | first mutable borrow occurs here | second mutable borrow occurs here - | - = note: due to object lifetime defaults, `Box LateLintPass<'b>>` actually means `Box<(dyn for<'b> LateLintPass<'b> + 'static)>` error[E0499]: cannot borrow `reg.sess_mut` as mutable more than once at a time --> $DIR/two-phase-surprise-no-conflict.rs:158:53 diff --git a/tests/ui/c-variadic/variadic-ffi-4.stderr b/tests/ui/c-variadic/variadic-ffi-4.stderr index c9d90d73dea3..fc9f8036083a 100644 --- a/tests/ui/c-variadic/variadic-ffi-4.stderr +++ b/tests/ui/c-variadic/variadic-ffi-4.stderr @@ -120,14 +120,14 @@ LL | } | - `ap1` dropped here while still borrowed error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:35:12 + --> $DIR/variadic-ffi-4.rs:35:5 | LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { | ------- ------- has type `VaListImpl<'2>` | | | has type `&mut VaListImpl<'1>` LL | *ap0 = ap1.clone(); - | ^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + | ^^^^ assignment requires that `'2` must outlive `'1` | = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` @@ -141,7 +141,7 @@ LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut | | | has type `&mut VaListImpl<'1>` LL | *ap0 = ap1.clone(); - | ^^^^^^^^^^^ argument requires that `'2` must outlive `'1` + | ^^^^^^^^^^^ argument requires that `'1` must outlive `'2` | = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs index 18566acc07fe..f8910c944c61 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs @@ -42,7 +42,7 @@ fn change_assoc_1<'a, 'b>( // This tests the default borrow check error, without the special casing for return values. fn require_static(_: *const dyn Trait<'static>) {} fn extend_to_static<'a>(ptr: *const dyn Trait<'a>) { - require_static(ptr as _) //~ error: lifetime may not live long enough + require_static(ptr as _) //~ error: borrowed data escapes outside of function } fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr index 6f590585c4a4..faaa6325f349 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr @@ -154,14 +154,22 @@ help: `'b` and `'a` must be the same: replace one with the other | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: lifetime may not live long enough - --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:45:20 +error[E0521]: borrowed data escapes outside of function + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:45:5 | LL | fn extend_to_static<'a>(ptr: *const dyn Trait<'a>) { - | -- lifetime `'a` defined here + | -- --- + | | | + | | `ptr` declared here, outside of the function body + | | `ptr` is a reference that is only valid in the function body + | lifetime `'a` defined here LL | require_static(ptr as _) - | ^^^^^^^^ cast requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `ptr` escapes the function body here + | argument requires that `'a` must outlive `'static` error: aborting due to 11 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0308, E0521. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/cfg/disallowed-cli-cfgs.emscripten_wasm_eh_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.emscripten_wasm_eh_.stderr new file mode 100644 index 000000000000..8b2ee0e5c0c3 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.emscripten_wasm_eh_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg emscripten_wasm_eh` flag + | + = note: config `emscripten_wasm_eh` is only supposed to be controlled by `-Z emscripten_wasm_eh` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.rs b/tests/ui/cfg/disallowed-cli-cfgs.rs index 3c9ee87f28ab..cae9c65cb45a 100644 --- a/tests/ui/cfg/disallowed-cli-cfgs.rs +++ b/tests/ui/cfg/disallowed-cli-cfgs.rs @@ -7,6 +7,7 @@ //@ revisions: target_has_atomic_equal_alignment_ target_has_atomic_load_store_ //@ revisions: target_thread_local_ relocation_model_ //@ revisions: fmt_debug_ +//@ revisions: emscripten_wasm_eh_ //@ [overflow_checks_]compile-flags: --cfg overflow_checks //@ [debug_assertions_]compile-flags: --cfg debug_assertions @@ -33,5 +34,6 @@ //@ [target_thread_local_]compile-flags: --cfg target_thread_local //@ [relocation_model_]compile-flags: --cfg relocation_model="a" //@ [fmt_debug_]compile-flags: --cfg fmt_debug="shallow" +//@ [emscripten_wasm_eh_]compile-flags: --cfg emscripten_wasm_eh fn main() {} diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index 70fec8a350aa..bf54d17f6ec8 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -202,6 +202,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `sme-lutv2` `sme2` `sme2p1` +`soft-float` `spe` `ssbs` `sse` diff --git a/tests/ui/const-generics/issues/issue-88119.stderr b/tests/ui/const-generics/issues/issue-88119.stderr index 370244fe8c98..f219c90849a2 100644 --- a/tests/ui/const-generics/issues/issue-88119.stderr +++ b/tests/ui/const-generics/issues/issue-88119.stderr @@ -11,12 +11,30 @@ error[E0284]: type annotations needed: cannot normalize `<&T as ConstName>::{con | LL | impl const ConstName for &T | ^^ cannot normalize `<&T as ConstName>::{constant#0}` + | +note: required for `&T` to implement `~const ConstName` + --> $DIR/issue-88119.rs:19:35 + | +LL | impl const ConstName for &T + | ^^^^^^^^^ ^^ +LL | where +LL | [(); name_len::()]:, + | --------------------- unsatisfied trait bound introduced here error[E0284]: type annotations needed: cannot normalize `<&mut T as ConstName>::{constant#0}` --> $DIR/issue-88119.rs:26:49 | LL | impl const ConstName for &mut T | ^^^^^^ cannot normalize `<&mut T as ConstName>::{constant#0}` + | +note: required for `&mut T` to implement `~const ConstName` + --> $DIR/issue-88119.rs:26:35 + | +LL | impl const ConstName for &mut T + | ^^^^^^^^^ ^^^^^^ +LL | where +LL | [(); name_len::()]:, + | --------------------- unsatisfied trait bound introduced here error: aborting due to 3 previous errors diff --git a/tests/ui/consts/const-eval/format.rs b/tests/ui/consts/const-eval/format.rs index e56d15e935bc..1878fc038276 100644 --- a/tests/ui/consts/const-eval/format.rs +++ b/tests/ui/consts/const-eval/format.rs @@ -1,13 +1,11 @@ const fn failure() { panic!("{:?}", 0); //~^ ERROR cannot call non-const formatting macro in constant functions - //~| ERROR cannot call non-const associated function `Arguments::<'_>::new_v1::<1, 1>` in constant functions } const fn print() { println!("{:?}", 0); //~^ ERROR cannot call non-const formatting macro in constant functions - //~| ERROR cannot call non-const associated function `Arguments::<'_>::new_v1::<2, 1>` in constant functions //~| ERROR cannot call non-const function `_print` in constant functions } diff --git a/tests/ui/consts/const-eval/format.stderr b/tests/ui/consts/const-eval/format.stderr index 25ed44e0f338..af90acc2a260 100644 --- a/tests/ui/consts/const-eval/format.stderr +++ b/tests/ui/consts/const-eval/format.stderr @@ -7,17 +7,8 @@ LL | panic!("{:?}", 0); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0015]: cannot call non-const associated function `Arguments::<'_>::new_v1::<1, 1>` in constant functions - --> $DIR/format.rs:2:5 - | -LL | panic!("{:?}", 0); - | ^^^^^^^^^^^^^^^^^ - | - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0015]: cannot call non-const formatting macro in constant functions - --> $DIR/format.rs:8:15 + --> $DIR/format.rs:7:15 | LL | println!("{:?}", 0); | ^^^^ @@ -25,17 +16,8 @@ LL | println!("{:?}", 0); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0015]: cannot call non-const associated function `Arguments::<'_>::new_v1::<2, 1>` in constant functions - --> $DIR/format.rs:8:5 - | -LL | println!("{:?}", 0); - | ^^^^^^^^^^^^^^^^^^^ - | - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0015]: cannot call non-const function `_print` in constant functions - --> $DIR/format.rs:8:5 + --> $DIR/format.rs:7:5 | LL | println!("{:?}", 0); | ^^^^^^^^^^^^^^^^^^^ @@ -43,6 +25,6 @@ LL | println!("{:?}", 0); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/coroutine/issue-52304.rs b/tests/ui/coroutine/issue-52304.rs index 552bc0028ee0..77dfe8391956 100644 --- a/tests/ui/coroutine/issue-52304.rs +++ b/tests/ui/coroutine/issue-52304.rs @@ -1,4 +1,6 @@ //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) #![feature(coroutines, coroutine_trait)] diff --git a/tests/ui/coroutine/resume-arg-outlives-2.rs b/tests/ui/coroutine/resume-arg-outlives-2.rs index 387b143ea279..a805cea9b7ea 100644 --- a/tests/ui/coroutine/resume-arg-outlives-2.rs +++ b/tests/ui/coroutine/resume-arg-outlives-2.rs @@ -18,8 +18,8 @@ fn demo<'not_static>(s: &'not_static str) -> thread::JoinHandle<()> { // exploit: generator.as_mut().resume(""); generator.as_mut().resume(s); // <- generator hoards it as `let ctx`. - //~^ ERROR borrowed data escapes outside of function thread::spawn(move || { + //~^ ERROR borrowed data escapes outside of function thread::sleep(time::Duration::from_millis(200)); generator.as_mut().resume(""); // <- resumes from the last `yield`, running `dbg!(ctx)`. }) diff --git a/tests/ui/coroutine/resume-arg-outlives-2.stderr b/tests/ui/coroutine/resume-arg-outlives-2.stderr index 3d630d7e7e48..92192d9a5572 100644 --- a/tests/ui/coroutine/resume-arg-outlives-2.stderr +++ b/tests/ui/coroutine/resume-arg-outlives-2.stderr @@ -1,16 +1,20 @@ error[E0521]: borrowed data escapes outside of function - --> $DIR/resume-arg-outlives-2.rs:20:5 + --> $DIR/resume-arg-outlives-2.rs:21:5 | -LL | fn demo<'not_static>(s: &'not_static str) -> thread::JoinHandle<()> { - | ----------- - `s` is a reference that is only valid in the function body - | | - | lifetime `'not_static` defined here +LL | fn demo<'not_static>(s: &'not_static str) -> thread::JoinHandle<()> { + | ----------- - `s` is a reference that is only valid in the function body + | | + | lifetime `'not_static` defined here ... -LL | generator.as_mut().resume(s); // <- generator hoards it as `let ctx`. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | `s` escapes the function body here - | argument requires that `'not_static` must outlive `'static` +LL | / thread::spawn(move || { +LL | | +LL | | thread::sleep(time::Duration::from_millis(200)); +LL | | generator.as_mut().resume(""); // <- resumes from the last `yield`, running `dbg!(ctx)`. +LL | | }) + | | ^ + | | | + | |______`s` escapes the function body here + | argument requires that `'not_static` must outlive `'static` error: aborting due to 1 previous error diff --git a/tests/ui/deprecation/deprecated_ar.rs b/tests/ui/deprecation/deprecated_ar.rs new file mode 100644 index 000000000000..404d062e6a44 --- /dev/null +++ b/tests/ui/deprecation/deprecated_ar.rs @@ -0,0 +1,4 @@ +//@ check-pass +//@ compile-flags: -Car=foo + +fn main() {} diff --git a/tests/ui/deprecation/deprecated_ar.stderr b/tests/ui/deprecation/deprecated_ar.stderr new file mode 100644 index 000000000000..de776c674996 --- /dev/null +++ b/tests/ui/deprecation/deprecated_ar.stderr @@ -0,0 +1,2 @@ +warning: `-C ar`: this option is deprecated and does nothing + diff --git a/tests/ui/deprecation/deprecated_inline_threshold.bad_val.stderr b/tests/ui/deprecation/deprecated_inline_threshold.bad_val.stderr new file mode 100644 index 000000000000..2e7a99010ae9 --- /dev/null +++ b/tests/ui/deprecation/deprecated_inline_threshold.bad_val.stderr @@ -0,0 +1,4 @@ +warning: `-C inline-threshold`: this option is deprecated and does nothing (consider using `-Cllvm-args=--inline-threshold=...`) + +error: incorrect value `asd` for codegen option `inline-threshold` - a number was expected + diff --git a/tests/ui/deprecation/deprecated_inline_threshold.good_val.stderr b/tests/ui/deprecation/deprecated_inline_threshold.good_val.stderr new file mode 100644 index 000000000000..2d6f3652d252 --- /dev/null +++ b/tests/ui/deprecation/deprecated_inline_threshold.good_val.stderr @@ -0,0 +1,2 @@ +warning: `-C inline-threshold`: this option is deprecated and does nothing (consider using `-Cllvm-args=--inline-threshold=...`) + diff --git a/tests/ui/deprecation/deprecated_inline_threshold.no_val.stderr b/tests/ui/deprecation/deprecated_inline_threshold.no_val.stderr new file mode 100644 index 000000000000..25104e637e00 --- /dev/null +++ b/tests/ui/deprecation/deprecated_inline_threshold.no_val.stderr @@ -0,0 +1,4 @@ +warning: `-C inline-threshold`: this option is deprecated and does nothing (consider using `-Cllvm-args=--inline-threshold=...`) + +error: codegen option `inline-threshold` requires a number (C inline-threshold=) + diff --git a/tests/ui/deprecation/deprecated_inline_threshold.rs b/tests/ui/deprecation/deprecated_inline_threshold.rs index 56e033b8cf54..b54fa36397af 100644 --- a/tests/ui/deprecation/deprecated_inline_threshold.rs +++ b/tests/ui/deprecation/deprecated_inline_threshold.rs @@ -1,4 +1,8 @@ -//@ check-pass -//@ compile-flags: -Cinline-threshold=666 +//@ revisions: good_val bad_val no_val +// +//@[good_val] compile-flags: -Cinline-threshold=666 +//@[good_val] check-pass +//@[bad_val] compile-flags: -Cinline-threshold=asd +//@[no_val] compile-flags: -Cinline-threshold fn main() {} diff --git a/tests/ui/deprecation/deprecated_inline_threshold.stderr b/tests/ui/deprecation/deprecated_inline_threshold.stderr deleted file mode 100644 index c4f8ff092b29..000000000000 --- a/tests/ui/deprecation/deprecated_inline_threshold.stderr +++ /dev/null @@ -1,2 +0,0 @@ -warning: the `-Cinline-threshold` flag is deprecated and does nothing (consider using `-Cllvm-args=--inline-threshold=...`) - diff --git a/tests/ui/deprecation/deprecated_no_stack_check_opt.rs b/tests/ui/deprecation/deprecated_no_stack_check_opt.rs new file mode 100644 index 000000000000..62584ec23e33 --- /dev/null +++ b/tests/ui/deprecation/deprecated_no_stack_check_opt.rs @@ -0,0 +1,4 @@ +//@ check-pass +//@ compile-flags: -Cno-stack-check + +fn main() {} diff --git a/tests/ui/deprecation/deprecated_no_stack_check_opt.stderr b/tests/ui/deprecation/deprecated_no_stack_check_opt.stderr new file mode 100644 index 000000000000..17efba787f07 --- /dev/null +++ b/tests/ui/deprecation/deprecated_no_stack_check_opt.stderr @@ -0,0 +1,2 @@ +warning: `-C no-stack-check`: this option is deprecated and does nothing + diff --git a/tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs new file mode 100644 index 000000000000..6f64d83f8a0c --- /dev/null +++ b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs @@ -0,0 +1,51 @@ +// Edition 2024 lint for change in drop order at tail expression +// This lint is to capture potential borrow-checking errors +// due to implementation of RFC 3606 +//@ edition: 2021 + +#![deny(tail_expr_drop_order)] //~ NOTE: the lint level is defined here + +fn should_lint_with_potential_borrowck_err() { + let _ = { String::new().as_str() }.len(); + //~^ ERROR: relative drop order changing + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: this temporary value will be dropped at the end of the block + //~| borrow later used by call + //~| NOTE: for more information, see +} + +fn should_lint_with_unsafe_block() { + fn f(_: usize) {} + f(unsafe { String::new().as_str() }.len()); + //~^ ERROR: relative drop order changing + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: this temporary value will be dropped at the end of the block + //~| borrow later used by call + //~| NOTE: for more information, see +} + +#[rustfmt::skip] +fn should_lint_with_big_block() { + fn f(_: T) {} + f({ + &mut || 0 + //~^ ERROR: relative drop order changing + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: this temporary value will be dropped at the end of the block + //~| borrow later used here + //~| NOTE: for more information, see + }) +} + +fn another_temp_that_is_copy_in_arg() { + fn f() {} + fn g(_: &()) {} + g({ &f() }); + //~^ ERROR: relative drop order changing + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: this temporary value will be dropped at the end of the block + //~| borrow later used by call + //~| NOTE: for more information, see +} + +fn main() {} diff --git a/tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr new file mode 100644 index 000000000000..a55e366dd0be --- /dev/null +++ b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr @@ -0,0 +1,52 @@ +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:9:15 + | +LL | let _ = { String::new().as_str() }.len(); + | ^^^^^^^^^^^^^ --- borrow later used by call + | | + | this temporary value will be dropped at the end of the block + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: the lint level is defined here + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:6:9 + | +LL | #![deny(tail_expr_drop_order)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:19:16 + | +LL | f(unsafe { String::new().as_str() }.len()); + | ^^^^^^^^^^^^^ --- borrow later used by call + | | + | this temporary value will be dropped at the end of the block + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see + +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:31:9 + | +LL | &mut || 0 + | ^^^^^^^^^ + | | + | this temporary value will be dropped at the end of the block + | borrow later used here + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see + +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:43:9 + | +LL | g({ &f() }); + | - ^^^^ this temporary value will be dropped at the end of the block + | | + | borrow later used by call + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see + +error: aborting due to 4 previous errors + diff --git a/tests/ui/drop/lint-tail-expr-drop-order.rs b/tests/ui/drop/lint-tail-expr-drop-order.rs index b2a5db0d8713..55a2d1d3b754 100644 --- a/tests/ui/drop/lint-tail-expr-drop-order.rs +++ b/tests/ui/drop/lint-tail-expr-drop-order.rs @@ -17,7 +17,6 @@ impl Drop for LoudDropper { //~| NOTE: `#1` invokes this custom destructor //~| NOTE: `x` invokes this custom destructor //~| NOTE: `#1` invokes this custom destructor - //~| NOTE: `future` invokes this custom destructor //~| NOTE: `_x` invokes this custom destructor //~| NOTE: `#1` invokes this custom destructor fn drop(&mut self) { diff --git a/tests/ui/drop/lint-tail-expr-drop-order.stderr b/tests/ui/drop/lint-tail-expr-drop-order.stderr index 92afae5af676..6ff9b7c12681 100644 --- a/tests/ui/drop/lint-tail-expr-drop-order.stderr +++ b/tests/ui/drop/lint-tail-expr-drop-order.stderr @@ -1,5 +1,5 @@ error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:41:15 + --> $DIR/lint-tail-expr-drop-order.rs:40:15 | LL | let x = LoudDropper; | - @@ -40,7 +40,7 @@ LL | #![deny(tail_expr_drop_order)] | ^^^^^^^^^^^^^^^^^^^^ error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:66:19 + --> $DIR/lint-tail-expr-drop-order.rs:65:19 | LL | let x = LoudDropper; | - @@ -76,7 +76,7 @@ LL | | } = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:93:7 + --> $DIR/lint-tail-expr-drop-order.rs:92:7 | LL | let x = LoudDropper; | - @@ -112,7 +112,7 @@ LL | | } = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:146:5 + --> $DIR/lint-tail-expr-drop-order.rs:145:5 | LL | let future = f(); | ------ @@ -136,19 +136,12 @@ note: `#1` invokes this custom destructor | LL | / impl Drop for LoudDropper { ... | -LL | | } - | |_^ -note: `future` invokes this custom destructor - --> $DIR/lint-tail-expr-drop-order.rs:10:1 - | -LL | / impl Drop for LoudDropper { -... | LL | | } | |_^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:163:14 + --> $DIR/lint-tail-expr-drop-order.rs:162:14 | LL | let x = T::default(); | - @@ -170,7 +163,7 @@ LL | } = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:177:5 + --> $DIR/lint-tail-expr-drop-order.rs:176:5 | LL | let x: Result = Ok(LoudDropper); | - @@ -206,7 +199,7 @@ LL | | } = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:221:5 + --> $DIR/lint-tail-expr-drop-order.rs:220:5 | LL | let x = LoudDropper2; | - @@ -226,7 +219,7 @@ LL | } = warning: this changes meaning in Rust 2024 = note: for more information, see note: `#1` invokes this custom destructor - --> $DIR/lint-tail-expr-drop-order.rs:194:5 + --> $DIR/lint-tail-expr-drop-order.rs:193:5 | LL | / impl Drop for LoudDropper3 { LL | | @@ -236,7 +229,7 @@ LL | | } LL | | } | |_____^ note: `x` invokes this custom destructor - --> $DIR/lint-tail-expr-drop-order.rs:206:5 + --> $DIR/lint-tail-expr-drop-order.rs:205:5 | LL | / impl Drop for LoudDropper2 { LL | | @@ -248,7 +241,7 @@ LL | | } = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:234:13 + --> $DIR/lint-tail-expr-drop-order.rs:233:13 | LL | LoudDropper.get() | ^^^^^^^^^^^ diff --git a/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr b/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr index d98100bc1b04..b0f971dd5cec 100644 --- a/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr +++ b/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr @@ -4,10 +4,14 @@ error: relative drop order changing in Rust 2024 LL | match func().await { | ^^^^^^^----- | | | + | | this value will be stored in a temporary; let us call it `#3` + | | up until Edition 2021 `#3` is dropped last but will be dropped earlier in Edition 2024 | | this value will be stored in a temporary; let us call it `#1` | | `#1` will be dropped later as of Edition 2024 | this value will be stored in a temporary; let us call it `#2` | up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024 + | `__awaitee` calls a custom destructor + | `__awaitee` will be dropped later as of Edition 2024 ... LL | Err(e) => {} | - diff --git a/tests/ui/drop/tail_expr_drop_order-on-recursive-boxed-fut.rs b/tests/ui/drop/tail_expr_drop_order-on-recursive-boxed-fut.rs new file mode 100644 index 000000000000..4a72f224d943 --- /dev/null +++ b/tests/ui/drop/tail_expr_drop_order-on-recursive-boxed-fut.rs @@ -0,0 +1,13 @@ +//@ edition: 2021 +//@ check-pass + +// Make sure we don't cycle error when normalizing types for tail expr drop order lint. + +#![deny(tail_expr_drop_order)] + +async fn test() -> Result<(), Box> { + Box::pin(test()).await?; + Ok(()) +} + +fn main() {} diff --git a/tests/ui/drop/tail_expr_drop_order-on-thread-local.rs b/tests/ui/drop/tail_expr_drop_order-on-thread-local.rs new file mode 100644 index 000000000000..e38175fd1b65 --- /dev/null +++ b/tests/ui/drop/tail_expr_drop_order-on-thread-local.rs @@ -0,0 +1,56 @@ +//@ check-pass + +#![feature(thread_local)] +#![deny(tail_expr_drop_order)] + +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; + +pub struct Global; + +#[thread_local] +static REENTRANCY_STATE: State = State { marker: PhantomData, controller: Global }; + +pub struct Token(PhantomData<*mut ()>); + +pub fn with_mut(f: impl FnOnce(&mut Token) -> T) -> T { + f(&mut REENTRANCY_STATE.borrow_mut()) +} + +pub struct State { + marker: PhantomData<*mut ()>, + controller: T, +} + +impl State { + pub fn borrow_mut(&self) -> TokenMut<'_, T> { + todo!() + } +} + +pub struct TokenMut<'a, T: ?Sized = Global> { + state: &'a State, + token: Token, +} + +impl Deref for TokenMut<'_, T> { + type Target = Token; + + fn deref(&self) -> &Self::Target { + todo!() + } +} + +impl DerefMut for TokenMut<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + todo!() + } +} + +impl Drop for TokenMut<'_, T> { + fn drop(&mut self) { + todo!() + } +} + +fn main() {} diff --git a/tests/ui/duplicate/multiple-types-with-same-name-and-derive.rs b/tests/ui/duplicate/multiple-types-with-same-name-and-derive.rs index 72375eb0b3e8..f3bf299aa65f 100644 --- a/tests/ui/duplicate/multiple-types-with-same-name-and-derive.rs +++ b/tests/ui/duplicate/multiple-types-with-same-name-and-derive.rs @@ -1,5 +1,5 @@ // Here, there are two types with the same name. One of these has a `derive` annotation, but in the -// expansion these `impl`s are associated to the the *other* type. There is a suggestion to remove +// expansion these `impl`s are associated to the *other* type. There is a suggestion to remove // unneeded type parameters, but because we're now point at a type with no type parameters, the // suggestion would suggest removing code from an empty span, which would ICE in nightly. // diff --git a/tests/ui/error-codes/E0229.stderr b/tests/ui/error-codes/E0229.stderr index ab2536cc0c9d..7c967e89ddbc 100644 --- a/tests/ui/error-codes/E0229.stderr +++ b/tests/ui/error-codes/E0229.stderr @@ -48,10 +48,10 @@ LL | fn baz(x: &>::A) {} | +++++ error[E0277]: the trait bound `I: Foo` is not satisfied - --> $DIR/E0229.rs:13:39 + --> $DIR/E0229.rs:13:14 | LL | fn baz(x: &>::A) {} - | ^^ the trait `Foo` is not implemented for `I` + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `I` | help: consider restricting type parameter `I` with trait `Foo` | diff --git a/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.rs b/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.rs new file mode 100644 index 000000000000..cff98b43fe74 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --check-cfg=cfg(emscripten_wasm_eh) +#[cfg(not(emscripten_wasm_eh))] +//~^ `cfg(emscripten_wasm_eh)` is experimental +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.stderr b/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.stderr new file mode 100644 index 000000000000..67769e3c7586 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.stderr @@ -0,0 +1,12 @@ +error[E0658]: `cfg(emscripten_wasm_eh)` is experimental and subject to change + --> $DIR/feature-gate-cfg-emscripten-wasm-eh.rs:2:11 + | +LL | #[cfg(not(emscripten_wasm_eh))] + | ^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(cfg_emscripten_wasm_eh)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.rs b/tests/ui/feature-gates/feature-gate-guard-patterns.rs index 929e8ef3181f..52ed89e668b1 100644 --- a/tests/ui/feature-gates/feature-gate-guard-patterns.rs +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.rs @@ -1,3 +1,5 @@ +#![allow(irrefutable_let_patterns)] + fn match_guards_still_work() { match 0 { 0 if guard(0) => {}, @@ -24,11 +26,9 @@ fn other_guards_dont() { if let (x if guard(x)) = 0 {} //~^ ERROR: guard patterns are experimental - //~| WARN: irrefutable while let (x if guard(x)) = 0 {} //~^ ERROR: guard patterns are experimental - //~| WARN: irrefutable #[cfg(FALSE)] while let (x if guard(x)) = 0 {} diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr index 0613b5c95a41..8b85b663889f 100644 --- a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr @@ -1,5 +1,5 @@ error: unexpected parentheses surrounding `match` arm pattern - --> $DIR/feature-gate-guard-patterns.rs:10:9 + --> $DIR/feature-gate-guard-patterns.rs:12:9 | LL | (0 if guard(0)) => {}, | ^ ^ @@ -11,7 +11,7 @@ LL + 0 if guard(0) => {}, | error[E0425]: cannot find value `x` in this scope - --> $DIR/feature-gate-guard-patterns.rs:21:22 + --> $DIR/feature-gate-guard-patterns.rs:23:22 | LL | let ((x if guard(x)) | x) = 0; | ^ not found in this scope @@ -23,13 +23,13 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) { | ^ | help: the binding `x` is available in a different scope in the same function - --> $DIR/feature-gate-guard-patterns.rs:21:11 + --> $DIR/feature-gate-guard-patterns.rs:23:11 | LL | let ((x if guard(x)) | x) = 0; | ^ error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:16:15 + --> $DIR/feature-gate-guard-patterns.rs:18:15 | LL | (0 if guard(0)) | 1 => {}, | ^^^^^^^^ @@ -40,7 +40,7 @@ LL | (0 if guard(0)) | 1 => {}, = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:21:16 + --> $DIR/feature-gate-guard-patterns.rs:23:16 | LL | let ((x if guard(x)) | x) = 0; | ^^^^^^^^ @@ -51,7 +51,7 @@ LL | let ((x if guard(x)) | x) = 0; = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:25:18 + --> $DIR/feature-gate-guard-patterns.rs:27:18 | LL | if let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -62,7 +62,7 @@ LL | if let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:29:21 + --> $DIR/feature-gate-guard-patterns.rs:30:21 | LL | while let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -94,26 +94,7 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider using match arm guards -warning: irrefutable `if let` pattern - --> $DIR/feature-gate-guard-patterns.rs:25:8 - | -LL | if let (x if guard(x)) = 0 {} - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this pattern will always match, so the `if let` is useless - = help: consider replacing the `if let` with a `let` - = note: `#[warn(irrefutable_let_patterns)]` on by default - -warning: irrefutable `while let` pattern - --> $DIR/feature-gate-guard-patterns.rs:29:11 - | -LL | while let (x if guard(x)) = 0 {} - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this pattern will always match, so the loop will never exit - = help: consider instead using a `loop { ... }` with a `let` inside it - -error: aborting due to 9 previous errors; 2 warnings emitted +error: aborting due to 9 previous errors Some errors have detailed explanations: E0425, E0658. For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/feature-gates/feature-gate-repr-simd.rs b/tests/ui/feature-gates/feature-gate-repr-simd.rs index 65ade97c7e1c..091dc479ef3d 100644 --- a/tests/ui/feature-gates/feature-gate-repr-simd.rs +++ b/tests/ui/feature-gates/feature-gate-repr-simd.rs @@ -1,9 +1,17 @@ -#[repr(simd)] //~ error: SIMD types are experimental +#[repr(simd)] //~ ERROR: SIMD types are experimental struct Foo([u64; 2]); #[repr(C)] //~ ERROR conflicting representation hints //~^ WARN this was previously accepted -#[repr(simd)] //~ error: SIMD types are experimental +#[repr(simd)] //~ ERROR: SIMD types are experimental struct Bar([u64; 2]); +#[repr(simd)] //~ ERROR: SIMD types are experimental +//~^ ERROR: attribute should be applied to a struct +union U {f: u32} + +#[repr(simd)] //~ ERROR: SIMD types are experimental +//~^ error: attribute should be applied to a struct +enum E { X } + fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-repr-simd.stderr b/tests/ui/feature-gates/feature-gate-repr-simd.stderr index 5a0ceb2dd74f..3bf8ec617059 100644 --- a/tests/ui/feature-gates/feature-gate-repr-simd.stderr +++ b/tests/ui/feature-gates/feature-gate-repr-simd.stderr @@ -18,6 +18,26 @@ LL | #[repr(simd)] = help: add `#![feature(repr_simd)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: SIMD types are experimental and possibly buggy + --> $DIR/feature-gate-repr-simd.rs:9:1 + | +LL | #[repr(simd)] + | ^^^^^^^^^^^^^ + | + = note: see issue #27731 for more information + = help: add `#![feature(repr_simd)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: SIMD types are experimental and possibly buggy + --> $DIR/feature-gate-repr-simd.rs:13:1 + | +LL | #[repr(simd)] + | ^^^^^^^^^^^^^ + | + = note: see issue #27731 for more information + = help: add `#![feature(repr_simd)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0566]: conflicting representation hints --> $DIR/feature-gate-repr-simd.rs:4:8 | @@ -31,10 +51,28 @@ LL | #[repr(simd)] = note: for more information, see issue #68585 = note: `#[deny(conflicting_repr_hints)]` on by default -error: aborting due to 3 previous errors +error[E0517]: attribute should be applied to a struct + --> $DIR/feature-gate-repr-simd.rs:9:8 + | +LL | #[repr(simd)] + | ^^^^ +LL | +LL | union U {f: u32} + | ---------------- not a struct -Some errors have detailed explanations: E0566, E0658. -For more information about an error, try `rustc --explain E0566`. +error[E0517]: attribute should be applied to a struct + --> $DIR/feature-gate-repr-simd.rs:13:8 + | +LL | #[repr(simd)] + | ^^^^ +LL | +LL | enum E { X } + | ------------ not a struct + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0517, E0566, E0658. +For more information about an error, try `rustc --explain E0517`. Future incompatibility report: Future breakage diagnostic: error[E0566]: conflicting representation hints --> $DIR/feature-gate-repr-simd.rs:4:8 diff --git a/tests/ui/fn/fn_def_coercion.rs b/tests/ui/fn/fn_def_coercion.rs index 313be6f28cdc..31c8fa41de17 100644 --- a/tests/ui/fn/fn_def_coercion.rs +++ b/tests/ui/fn/fn_def_coercion.rs @@ -46,12 +46,12 @@ fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { fn k<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { let x = match true { - true => foo::<&'c ()>, + true => foo::<&'c ()>, //~ ERROR lifetime may not live long enough false => foo::<&'a ()>, //~ ERROR lifetime may not live long enough }; x(a); - x(b); //~ ERROR lifetime may not live long enough + x(b); x(c); } diff --git a/tests/ui/fn/fn_def_coercion.stderr b/tests/ui/fn/fn_def_coercion.stderr index ec4a1bde7fd6..c2776887b79d 100644 --- a/tests/ui/fn/fn_def_coercion.stderr +++ b/tests/ui/fn/fn_def_coercion.stderr @@ -6,9 +6,9 @@ LL | fn f<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | | | lifetime `'a` defined here LL | let mut x = foo::<&'a ()>; - | ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b` + | ^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` | - = help: consider adding the following bound: `'a: 'b` + = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a function pointer to `foo` = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance @@ -22,9 +22,9 @@ LL | fn f<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | lifetime `'a` defined here LL | let mut x = foo::<&'a ()>; LL | x = foo::<&'b ()>; - | ^^^^^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b` | - = help: consider adding the following bound: `'b: 'a` + = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of a function pointer to `foo` = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance @@ -53,9 +53,9 @@ LL | fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | lifetime `'a` defined here LL | let mut x = foo::<&'c ()>; LL | x = foo::<&'b ()>; - | ^^^^^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b` | - = help: consider adding the following bound: `'b: 'a` + = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of a function pointer to `foo` = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance @@ -69,9 +69,9 @@ LL | fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | lifetime `'a` defined here ... LL | x = foo::<&'a ()>; - | ^^^^^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b` + | ^^^^^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` | - = help: consider adding the following bound: `'a: 'b` + = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a function pointer to `foo` = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance @@ -89,9 +89,9 @@ LL | fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | lifetime `'a` defined here LL | let x = match true { LL | true => foo::<&'b ()>, - | ^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b` | - = help: consider adding the following bound: `'b: 'a` + = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of a function pointer to `foo` = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance @@ -105,9 +105,9 @@ LL | fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | lifetime `'a` defined here ... LL | false => foo::<&'a ()>, - | ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b` + | ^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` | - = help: consider adding the following bound: `'a: 'b` + = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a function pointer to `foo` = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance @@ -117,15 +117,15 @@ help: `'a` and `'b` must be the same: replace one with the other = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: lifetime may not live long enough - --> $DIR/fn_def_coercion.rs:50:18 + --> $DIR/fn_def_coercion.rs:49:17 | LL | fn k<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | -- -- lifetime `'c` defined here | | | lifetime `'a` defined here -... -LL | false => foo::<&'a ()>, - | ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'c` +LL | let x = match true { +LL | true => foo::<&'c ()>, + | ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'c` | = help: consider adding the following bound: `'a: 'c` = note: requirement occurs because of a function pointer to `foo` @@ -133,17 +133,20 @@ LL | false => foo::<&'a ()>, = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/fn_def_coercion.rs:54:5 + --> $DIR/fn_def_coercion.rs:50:18 | LL | fn k<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here ... -LL | x(b); - | ^^^^ argument requires that `'b` must outlive `'a` +LL | false => foo::<&'a ()>, + | ^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a function pointer to `foo` + = note: the function `foo` is invariant over the parameter `T` + = help: see for more information about variance help: the following changes may resolve your lifetime errors | diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-2.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type-2.stderr index e57b5af82cde..85e3452fbf2c 100644 --- a/tests/ui/fn/implied-bounds-unnorm-associated-type-2.stderr +++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-2.stderr @@ -6,7 +6,7 @@ LL | fn g<'a, 'b>() { | | | lifetime `'a` defined here LL | f::<'a, 'b>(()); - | ^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^^^ generic argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a function pointer to `f` diff --git a/tests/ui/impl-trait/multiple-lifetimes/error-handling.stderr b/tests/ui/impl-trait/multiple-lifetimes/error-handling.stderr index 00709ee7438a..945fb0fc618f 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/error-handling.stderr +++ b/tests/ui/impl-trait/multiple-lifetimes/error-handling.stderr @@ -6,6 +6,9 @@ LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { | | | lifetime `'a` defined here ... +LL | let u = v; + | - requirement occurs due to copying this value +... LL | let _: &'b i32 = *u.0; | ^^^^^^^ type annotation requires that `'a` must outlive `'b` | diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.rs b/tests/ui/impl-trait/precise-capturing/migration-note.rs index 36db07e5764f..7587e89409aa 100644 --- a/tests/ui/impl-trait/precise-capturing/migration-note.rs +++ b/tests/ui/impl-trait/precise-capturing/migration-note.rs @@ -29,11 +29,11 @@ fn needs_static() { let a = display_len(&x); //~^ ERROR `x` does not live long enough //~| NOTE this call may capture more lifetimes than intended - //~| NOTE argument requires that `x` is borrowed for `'static` //~| NOTE borrowed value does not live long enoug fn needs_static(_: impl Sized + 'static) {} needs_static(a); + //~^ NOTE argument requires that `x` is borrowed for `'static` } //~^ NOTE `x` dropped here while still borrowed @@ -76,11 +76,11 @@ fn needs_static_mut() { let a = display_len_mut(&mut x); //~^ ERROR `x` does not live long enough //~| NOTE this call may capture more lifetimes than intended - //~| NOTE argument requires that `x` is borrowed for `'static` //~| NOTE borrowed value does not live long enough fn needs_static(_: impl Sized + 'static) {} needs_static(a); + //~^ NOTE argument requires that `x` is borrowed for `'static` } //~^ NOTE `x` dropped here while still borrowed diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.stderr b/tests/ui/impl-trait/precise-capturing/migration-note.stderr index c9403532dfa3..9caf7a201ef8 100644 --- a/tests/ui/impl-trait/precise-capturing/migration-note.stderr +++ b/tests/ui/impl-trait/precise-capturing/migration-note.stderr @@ -42,11 +42,11 @@ LL | let x = vec![1]; | - binding `x` declared here LL | LL | let a = display_len(&x); - | ------------^^- - | | | - | | borrowed value does not live long enough - | argument requires that `x` is borrowed for `'static` + | ^^ borrowed value does not live long enough ... +LL | needs_static(a); + | --------------- argument requires that `x` is borrowed for `'static` +LL | LL | } | - `x` dropped here while still borrowed | @@ -118,11 +118,11 @@ LL | let mut x = vec![1]; | ----- binding `x` declared here LL | LL | let a = display_len_mut(&mut x); - | ----------------^^^^^^- - | | | - | | borrowed value does not live long enough - | argument requires that `x` is borrowed for `'static` + | ^^^^^^ borrowed value does not live long enough ... +LL | needs_static(a); + | --------------- argument requires that `x` is borrowed for `'static` +LL | LL | } | - `x` dropped here while still borrowed | diff --git a/tests/ui/inline-const/const-expr-lifetime-err.stderr b/tests/ui/inline-const/const-expr-lifetime-err.stderr index be3fc8d28c51..031bf98262a8 100644 --- a/tests/ui/inline-const/const-expr-lifetime-err.stderr +++ b/tests/ui/inline-const/const-expr-lifetime-err.stderr @@ -6,10 +6,9 @@ LL | fn foo<'a>() { LL | let y = (); | - binding `y` declared here LL | equate(InvariantRef::new(&y), const { InvariantRef::<'a>::NEW }); - | ------------------^^- - | | | - | | borrowed value does not live long enough - | argument requires that `y` is borrowed for `'a` + | ^^ ----------------------- using this value as a constant requires that `y` is borrowed for `'a` + | | + | borrowed value does not live long enough LL | LL | } | - `y` dropped here while still borrowed diff --git a/tests/ui/inline-const/const-match-pat-lifetime-err.stderr b/tests/ui/inline-const/const-match-pat-lifetime-err.stderr index 95fe7085e502..7eea1846057a 100644 --- a/tests/ui/inline-const/const-match-pat-lifetime-err.stderr +++ b/tests/ui/inline-const/const-match-pat-lifetime-err.stderr @@ -9,7 +9,7 @@ LL | match InvariantRef::new(&y) { | ^^ borrowed value does not live long enough LL | LL | const { InvariantRef::<'a>::NEW } => (), - | --------------------------------- type annotation requires that `y` is borrowed for `'a` + | ----------------------- using this value as a constant requires that `y` is borrowed for `'a` LL | } LL | } | - `y` dropped here while still borrowed diff --git a/tests/ui/issues/issue-18611.stderr b/tests/ui/issues/issue-18611.stderr index 918654215b37..4fa699de6352 100644 --- a/tests/ui/issues/issue-18611.stderr +++ b/tests/ui/issues/issue-18611.stderr @@ -11,19 +11,17 @@ LL | trait HasState { | ^^^^^^^^^^^^^^ error[E0277]: the trait bound `isize: HasState` is not satisfied - --> $DIR/issue-18611.rs:1:46 + --> $DIR/issue-18611.rs:1:18 | -LL | fn add_state(op: ::State) { - | ______________________________________________^ -... | -LL | | } - | |_^ the trait `HasState` is not implemented for `isize` +LL | fn add_state(op: ::State) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HasState` is not implemented for `isize` | help: this trait has no implementations, consider adding one --> $DIR/issue-18611.rs:6:1 | LL | trait HasState { | ^^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-35570.stderr b/tests/ui/issues/issue-35570.stderr index 0aa6b5e402e4..b39b15fdf4b1 100644 --- a/tests/ui/issues/issue-35570.stderr +++ b/tests/ui/issues/issue-35570.stderr @@ -11,15 +11,10 @@ LL | trait Trait2<'a> { | ^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `for<'a> (): Trait2<'a>` is not satisfied - --> $DIR/issue-35570.rs:8:66 + --> $DIR/issue-35570.rs:8:16 | -LL | fn _ice(param: Box Trait1<<() as Trait2<'a>>::Ty>>) { - | __________________________________________________________________^ -LL | | -LL | | -LL | | let _e: (usize, usize) = unsafe{mem::transmute(param)}; -LL | | } - | |_^ the trait `for<'a> Trait2<'a>` is not implemented for `()` +LL | fn _ice(param: Box Trait1<<() as Trait2<'a>>::Ty>>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Trait2<'a>` is not implemented for `()` | help: this trait has no implementations, consider adding one --> $DIR/issue-35570.rs:4:1 diff --git a/tests/ui/kindck/kindck-impl-type-params.stderr b/tests/ui/kindck/kindck-impl-type-params.stderr index a0a4ef09216f..d375aa9cb596 100644 --- a/tests/ui/kindck/kindck-impl-type-params.stderr +++ b/tests/ui/kindck/kindck-impl-type-params.stderr @@ -112,13 +112,13 @@ LL | struct Foo; // does not impl Copy | error: lifetime may not live long enough - --> $DIR/kindck-impl-type-params.rs:30:19 + --> $DIR/kindck-impl-type-params.rs:30:13 | LL | fn foo<'a>() { | -- lifetime `'a` defined here LL | let t: S<&'a isize> = S(marker::PhantomData); LL | let a = &t as &dyn Gettable<&'a isize>; - | ^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static` error: aborting due to 7 previous errors diff --git a/tests/ui/lifetimes/copy_modulo_regions.stderr b/tests/ui/lifetimes/copy_modulo_regions.stderr index 310ddb21647f..0d69f0323d62 100644 --- a/tests/ui/lifetimes/copy_modulo_regions.stderr +++ b/tests/ui/lifetimes/copy_modulo_regions.stderr @@ -4,7 +4,10 @@ error: lifetime may not live long enough LL | fn foo<'a>() -> [Foo<'a>; 100] { | -- lifetime `'a` defined here LL | [mk_foo::<'a>(); 100] - | ^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^ + | | + | returning this value requires that `'a` must outlive `'static` + | requirement occurs due to copying this value | = note: requirement occurs because of the type `Foo<'_>`, which makes the generic argument `'_` invariant = note: the struct `Foo<'a>` is invariant over the parameter `'a` diff --git a/tests/ui/lifetimes/issue-90170-elision-mismatch.stderr b/tests/ui/lifetimes/issue-90170-elision-mismatch.stderr index 82511d07b0ef..5e16c57a618f 100644 --- a/tests/ui/lifetimes/issue-90170-elision-mismatch.stderr +++ b/tests/ui/lifetimes/issue-90170-elision-mismatch.stderr @@ -7,6 +7,9 @@ LL | pub fn foo(x: &mut Vec<&u8>, y: &u8) { x.push(y); } | | let's call the lifetime of this reference `'1` | let's call the lifetime of this reference `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | pub fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } @@ -21,6 +24,9 @@ LL | pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); } | | let's call the lifetime of this reference `'1` | let's call the lifetime of this reference `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | pub fn foo2<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } @@ -35,6 +41,9 @@ LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); } | | let's call the lifetime of this reference `'1` | let's call the lifetime of this reference `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider reusing a named lifetime parameter | LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } diff --git a/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.rs b/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.rs index ce4cddc9b39b..30a1811fee54 100644 --- a/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.rs +++ b/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.rs @@ -7,9 +7,9 @@ fn inner(mut foo: &[u8]) { let refcell = RefCell::new(&mut foo); //~^ ERROR `foo` does not live long enough let read = &refcell as &RefCell; - //~^ ERROR lifetime may not live long enough read_thing(read); + //~^ ERROR borrowed data escapes outside of function } fn read_thing(refcell: &RefCell) {} diff --git a/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.stderr b/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.stderr index e4cd54ac3374..4df2e906e222 100644 --- a/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.stderr +++ b/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.stderr @@ -5,22 +5,32 @@ LL | fn inner(mut foo: &[u8]) { | ------- binding `foo` declared here LL | let refcell = RefCell::new(&mut foo); | ^^^^^^^^ borrowed value does not live long enough -LL | -LL | let read = &refcell as &RefCell; - | ------------------------------ cast requires that `foo` is borrowed for `'static` ... +LL | read_thing(read); + | ---------------- argument requires that `foo` is borrowed for `'static` +LL | LL | } | - `foo` dropped here while still borrowed -error: lifetime may not live long enough - --> $DIR/issue-90600-expected-return-static-indirect.rs:9:16 +error[E0521]: borrowed data escapes outside of function + --> $DIR/issue-90600-expected-return-static-indirect.rs:11:5 | LL | fn inner(mut foo: &[u8]) { - | - let's call the lifetime of this reference `'1` + | ------- - let's call the lifetime of this reference `'1` + | | + | `foo` is a reference that is only valid in the function body ... -LL | let read = &refcell as &RefCell; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static` +LL | read_thing(read); + | ^^^^^^^^^^^^^^^^ + | | + | `foo` escapes the function body here + | argument requires that `'1` must outlive `'static` + | + = note: requirement occurs because of the type `RefCell<(dyn std::io::Read + 'static)>`, which makes the generic argument `(dyn std::io::Read + 'static)` invariant + = note: the struct `RefCell` is invariant over the parameter `T` + = help: see for more information about variance error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0597`. +Some errors have detailed explanations: E0521, E0597. +For more information about an error, try `rustc --explain E0521`. diff --git a/tests/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.stderr b/tests/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.stderr index 6f7127d4c4c4..a187cb755dd5 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.stderr @@ -8,6 +8,9 @@ LL | fn foo(x: &mut Vec>, y: Ref) { LL | x.push(y); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(x: &mut Vec>, y: Ref<'a, i32>) { diff --git a/tests/ui/lifetimes/lifetime-errors/ex2c-push-inference-variable.stderr b/tests/ui/lifetimes/lifetime-errors/ex2c-push-inference-variable.stderr index cace80272f5b..610a669cdedd 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex2c-push-inference-variable.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex2c-push-inference-variable.stderr @@ -10,6 +10,9 @@ LL | x.push(z); | ^^^^^^^^^ argument requires that `'c` must outlive `'b` | = help: consider adding the following bound: `'c: 'b` + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.rs b/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.rs index f573230293ee..4cd06e1c02b2 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.rs +++ b/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.rs @@ -4,9 +4,9 @@ struct Ref<'a, T: 'a> { fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { let a: &mut Vec> = x; + //~^ ERROR lifetime may not live long enough let b = Ref { data: y.data }; a.push(b); - //~^ ERROR lifetime may not live long enough } fn main() { } diff --git a/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.stderr b/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.stderr index 4a981e4de604..0da32aacdb3d 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.stderr @@ -1,15 +1,17 @@ error: lifetime may not live long enough - --> $DIR/ex2d-push-inference-variable-2.rs:8:5 + --> $DIR/ex2d-push-inference-variable-2.rs:6:33 | LL | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { | -- -- lifetime `'c` defined here | | | lifetime `'b` defined here -... -LL | a.push(b); - | ^^^^^^^^^ argument requires that `'c` must outlive `'b` +LL | let a: &mut Vec> = x; + | ^ assignment requires that `'c` must outlive `'b` | = help: consider adding the following bound: `'c: 'b` + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.rs b/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.rs index 4a934bbf080d..498cea368247 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.rs +++ b/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.rs @@ -4,9 +4,9 @@ struct Ref<'a, T: 'a> { fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { let a: &mut Vec> = x; + //~^ ERROR lifetime may not live long enough let b = Ref { data: y.data }; Vec::push(a, b); - //~^ ERROR lifetime may not live long enough } fn main() { } diff --git a/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.stderr b/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.stderr index 2bd047113bca..4474a898fdc6 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.stderr @@ -1,15 +1,17 @@ error: lifetime may not live long enough - --> $DIR/ex2e-push-inference-variable-3.rs:8:5 + --> $DIR/ex2e-push-inference-variable-3.rs:6:33 | LL | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { | -- -- lifetime `'c` defined here | | | lifetime `'b` defined here -... -LL | Vec::push(a, b); - | ^^^^^^^^^^^^^^^ argument requires that `'c` must outlive `'b` +LL | let a: &mut Vec> = x; + | ^ assignment requires that `'c` must outlive `'b` | = help: consider adding the following bound: `'c: 'b` + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.stderr index 6ba130308a33..c67ea19effc0 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.stderr @@ -8,6 +8,9 @@ LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) { LL | z.push((x,y)); | ^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec<(&u8, &u8)>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(z: &mut Vec<(&'a u8,&u8)>, (x, y): (&'a u8, &u8)) { @@ -23,6 +26,9 @@ LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) { LL | z.push((x,y)); | ^^^^^^^^^^^^^ argument requires that `'3` must outlive `'4` | + = note: requirement occurs because of a mutable reference to `Vec<(&u8, &u8)>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(z: &mut Vec<(&u8,&'a u8)>, (x, y): (&u8, &'a u8)) { diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.stderr index 352619c0ffc3..0980b37e535a 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.stderr @@ -10,6 +10,9 @@ LL | x.push(y); | ^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.stderr index 16cf009ee48b..16cd47420a58 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.stderr @@ -9,6 +9,9 @@ LL | x.push(y); | ^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr index 017bfa714631..264673ff3e82 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr @@ -8,6 +8,9 @@ LL | fn foo(mut x: Vec, y: Ref) { LL | x.push(y); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(mut x: Vec>, y: Ref<'a>) { diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-latebound-regions.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-latebound-regions.stderr index 080eb43cecbc..8552755d1685 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-latebound-regions.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-latebound-regions.stderr @@ -9,6 +9,9 @@ LL | x.push(y); | ^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.stderr index cb629d2e3d3f..2a2cf6508fdb 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.stderr @@ -19,6 +19,9 @@ LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) { LL | y.push(z); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(x:fn(&u8, &u8), y: Vec<&'a u8>, z: &'a u8) { diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.stderr index 420cfa6b569b..01bfe7829206 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.stderr @@ -8,6 +8,9 @@ LL | fn foo(x: &mut Vec<&u8>, y: &u8) { LL | x.push(y); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter and update trait if needed | LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.stderr index 05f9308124b1..41154755b5d8 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.stderr @@ -19,6 +19,9 @@ LL | fn foo(x:Box , y: Vec<&u8>, z: &u8) { LL | y.push(z); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(x:Box , y: Vec<&'a u8>, z: &'a u8) { diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.stderr index 875d22576e58..10e8ca852f0f 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.stderr @@ -8,6 +8,9 @@ LL | fn foo(x: &mut Vec<&u8>, y: &u8) { LL | x.push(y); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { diff --git a/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr b/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr index 6770da091ce8..09801a1aad24 100644 --- a/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr +++ b/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr @@ -2,11 +2,10 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/tail-expr-in-nested-expr.rs:4:15 | LL | let _ = { String::new().as_str() }.len(); - | ^^^^^^^^^^^^^--------- + | ^^^^^^^^^^^^^ - --- borrow later used by call | | | | | temporary value is freed at the end of this statement | creates a temporary value which is freed while still in use - | borrow later used here | = note: consider using a `let` binding to create a longer lived value diff --git a/tests/ui/lint/lint-const-item-mutation.stderr b/tests/ui/lint/lint-const-item-mutation.stderr index 747c38b80076..0e405c306fe4 100644 --- a/tests/ui/lint/lint-const-item-mutation.stderr +++ b/tests/ui/lint/lint-const-item-mutation.stderr @@ -75,10 +75,15 @@ warning: taking a mutable reference to a `const` item --> $DIR/lint-const-item-mutation.rs:42:5 | LL | (&mut MY_STRUCT).use_mut(); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item +note: mutable reference created due to call to this method + --> $DIR/lint-const-item-mutation.rs:9:5 + | +LL | fn use_mut(&mut self) {} + | ^^^^^^^^^^^^^^^^^^^^^ note: `const` item defined here --> $DIR/lint-const-item-mutation.rs:27:1 | diff --git a/tests/ui/moves/move-deref-coercion.stderr b/tests/ui/moves/move-deref-coercion.stderr index 5760f4a7fdc3..25639075a3f4 100644 --- a/tests/ui/moves/move-deref-coercion.stderr +++ b/tests/ui/moves/move-deref-coercion.stderr @@ -4,7 +4,7 @@ error[E0382]: borrow of partially moved value: `val` LL | let _val = val.first; | --------- value partially moved here LL | val.inner; - | ^^^^^^^^^ value borrowed here after partial move + | ^^^ value borrowed here after partial move | = note: partial move occurs because `val.first` has type `NotCopy`, which does not implement the `Copy` trait = note: borrow occurs due to deref coercion to `NotCopy` @@ -20,7 +20,7 @@ error[E0382]: borrow of partially moved value: `val` LL | let _val = val.first; | --------- value partially moved here LL | val.inner_method(); - | ^^^^^^^^^^^^^^^^^^ value borrowed here after partial move + | ^^^ value borrowed here after partial move | = note: partial move occurs because `val.first` has type `NotCopy`, which does not implement the `Copy` trait = note: borrow occurs due to deref coercion to `NotCopy` diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index f37ce967a1ba..60087ec992bd 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -23,6 +23,10 @@ LL | |_outlives1, _outlives2, _outlives3, x, y| { ... LL | demand_y(x, y, p) | ^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + | + = note: requirement occurs because of the type `Cell<&'?34 u32>`, which makes the generic argument `&'?34 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance note: no external requirements --> $DIR/propagate-approximated-fail-no-postdom.rs:38:1 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-ref.rs b/tests/ui/nll/closure-requirements/propagate-approximated-ref.rs index 1c27e38f8321..f4db723704cb 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-ref.rs +++ b/tests/ui/nll/closure-requirements/propagate-approximated-ref.rs @@ -41,9 +41,10 @@ fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u3 #[rustc_regions] fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { + //~^ ERROR lifetime may not live long enough + // Only works if 'x: 'y: demand_y(x, y, x.get()) - //~^ ERROR lifetime may not live long enough }); } diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr index e2d0b105ab14..7325a9de8b29 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -23,17 +23,21 @@ LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { = note: defining type: supply error: lifetime may not live long enough - --> $DIR/propagate-approximated-ref.rs:45:9 + --> $DIR/propagate-approximated-ref.rs:43:5 | -LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | demand_y(x, y, x.get()) - | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'b` +LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | / establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { +... | +LL | | }); + | |______^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of the type `Cell<&'?11 u32>`, which makes the generic argument `&'?11 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index d7933a39eaac..0094d7a50d36 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -19,6 +19,10 @@ LL | foo(cell, |cell_a, cell_x| { | `cell_a` declared here, outside of the closure body LL | cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure | ^^^^^^^^^^^^^^^^^^^^^^^^ `cell_x` escapes the closure body here + | + = note: requirement occurs because of the type `Cell<&'?8 u32>`, which makes the generic argument `&'?8 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance note: no external requirements --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:18:1 @@ -56,11 +60,11 @@ error[E0597]: `a` does not live long enough LL | let a = 0; | - binding `a` declared here LL | let cell = Cell::new(&a); - | ----------^^- - | | | - | | borrowed value does not live long enough - | argument requires that `a` is borrowed for `'static` + | ^^ borrowed value does not live long enough ... +LL | cell_x.set(cell_a.get()); // forces 'a: 'x, implies 'a = 'static -> borrow error + | ------------------------ argument requires that `a` is borrowed for `'static` +LL | }) LL | } | - `a` dropped here while still borrowed diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs index 25e212a72256..afabb69ec4cd 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs @@ -30,10 +30,9 @@ fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u3 #[rustc_regions] fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { - //~^ ERROR borrowed data escapes outside of function - // Only works if 'x: 'y: demand_y(x, y, x.get()) + //~^ ERROR borrowed data escapes outside of function }); } diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index 3c04cf300ad4..b9365c94a1be 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -23,23 +23,18 @@ LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { = note: defining type: supply error[E0521]: borrowed data escapes outside of function - --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:32:5 + --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:34:9 | -LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { - | -- ------ `cell_a` is a reference that is only valid in the function body - | | - | lifetime `'a` defined here -LL | / establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { -... | -LL | | }); - | | ^ - | | | - | |______`cell_a` escapes the function body here - | argument requires that `'a` must outlive `'static` - | - = note: requirement occurs because of the type `Cell<&'?9 u32>`, which makes the generic argument `&'?9 u32` invariant - = note: the struct `Cell` is invariant over the parameter `T` - = help: see for more information about variance +LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- ------ `cell_a` is a reference that is only valid in the function body + | | + | lifetime `'a` defined here +... +LL | demand_y(x, y, x.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `cell_a` escapes the function body here + | argument requires that `'a` must outlive `'static` error: aborting due to 1 previous error diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs index cda7b22362f3..9f3e80d3db66 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs @@ -33,10 +33,9 @@ fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u3 #[rustc_regions] fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - //~^ ERROR borrowed data escapes outside of function - // Only works if 'x: 'y: demand_y(x, y, x.get()) + //~^ ERROR borrowed data escapes outside of function }); } diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index 9f5762ccbfa6..e5d2867103cf 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -23,23 +23,18 @@ LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { = note: defining type: supply error[E0521]: borrowed data escapes outside of function - --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:35:5 + --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:37:9 | -LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { - | -- ------ `cell_a` is a reference that is only valid in the function body - | | - | lifetime `'a` defined here -LL | / establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { -... | -LL | | }); - | | ^ - | | | - | |______`cell_a` escapes the function body here - | argument requires that `'a` must outlive `'static` - | - = note: requirement occurs because of the type `Cell<&'?10 u32>`, which makes the generic argument `&'?10 u32` invariant - = note: the struct `Cell` is invariant over the parameter `T` - = help: see for more information about variance +LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- ------ `cell_a` is a reference that is only valid in the function body + | | + | lifetime `'a` defined here +... +LL | demand_y(x, y, x.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `cell_a` escapes the function body here + | argument requires that `'a` must outlive `'static` error: aborting due to 1 previous error diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-val.rs b/tests/ui/nll/closure-requirements/propagate-approximated-val.rs index e7e2f157604b..4d663c53d27a 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-val.rs +++ b/tests/ui/nll/closure-requirements/propagate-approximated-val.rs @@ -34,9 +34,10 @@ fn demand_y<'x, 'y>(_outlives1: Cell<&&'x u32>, _outlives2: Cell<&'y &u32>, _y: #[rustc_regions] fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { + //~^ ERROR lifetime may not live long enough + // Only works if 'x: 'y: demand_y(outlives1, outlives2, x.get()) - //~^ ERROR lifetime may not live long enough }); } diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr index 4787577a6e1a..a14bfb06e831 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -23,17 +23,21 @@ LL | fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { = note: defining type: test error: lifetime may not live long enough - --> $DIR/propagate-approximated-val.rs:38:9 + --> $DIR/propagate-approximated-val.rs:36:5 | -LL | fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | demand_y(outlives1, outlives2, x.get()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'b` +LL | fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | / establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { +... | +LL | | }); + | |______^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of the type `Cell<&'?7 u32>`, which makes the generic argument `&'?7 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index 669b56a0be70..4558ff50674f 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -22,6 +22,10 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { LL | // Only works if 'x: 'y: LL | demand_y(x, y, x.get()) | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + | + = note: requirement occurs because of the type `Cell<&'?37 u32>`, which makes the generic argument `&'?37 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance note: no external requirements --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:34:1 diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index 75f476ac5f18..83173ae80c00 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -22,6 +22,10 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y LL | // Only works if 'x: 'y: LL | demand_y(x, y, x.get()) | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + | + = note: requirement occurs because of the type `Cell<&'?43 u32>`, which makes the generic argument `&'?43 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance note: no external requirements --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:38:1 diff --git a/tests/ui/nll/issue-54779-anon-static-lifetime.rs b/tests/ui/nll/issue-54779-anon-static-lifetime.rs index 260b6b109ca9..6b8fa608ebbb 100644 --- a/tests/ui/nll/issue-54779-anon-static-lifetime.rs +++ b/tests/ui/nll/issue-54779-anon-static-lifetime.rs @@ -29,7 +29,7 @@ impl DebugWith for Foo { fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { let Foo { bar } = self; - bar.debug_with(cx); //~ ERROR: lifetime may not live long enough + bar.debug_with(cx); //~ borrowed data escapes outside of method Ok(()) } } diff --git a/tests/ui/nll/issue-54779-anon-static-lifetime.stderr b/tests/ui/nll/issue-54779-anon-static-lifetime.stderr index a454ed265685..03a559066142 100644 --- a/tests/ui/nll/issue-54779-anon-static-lifetime.stderr +++ b/tests/ui/nll/issue-54779-anon-static-lifetime.stderr @@ -1,11 +1,17 @@ -error: lifetime may not live long enough - --> $DIR/issue-54779-anon-static-lifetime.rs:32:24 +error[E0521]: borrowed data escapes outside of method + --> $DIR/issue-54779-anon-static-lifetime.rs:32:9 | LL | cx: &dyn DebugContext, - | - let's call the lifetime of this reference `'1` + | -- - let's call the lifetime of this reference `'1` + | | + | `cx` is a reference that is only valid in the method body ... LL | bar.debug_with(cx); - | ^^ coercion requires that `'1` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^ + | | + | `cx` escapes the method body here + | argument requires that `'1` must outlive `'static` error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/nll/issue-62007-assign-const-index.stderr b/tests/ui/nll/issue-62007-assign-const-index.stderr index 0db9fe62c386..32716c47cd4d 100644 --- a/tests/ui/nll/issue-62007-assign-const-index.stderr +++ b/tests/ui/nll/issue-62007-assign-const-index.stderr @@ -17,10 +17,9 @@ LL | fn to_refs(mut list: [&mut List; 2]) -> Vec<&mut T> { | - let's call the lifetime of this reference `'1` ... LL | if let Some(n) = list[0].next.as_mut() { - | ^^^^^^^^^^^^--------- - | | - | `list[_].next` was mutably borrowed here in the previous iteration of the loop - | argument requires that `list[_].next` is borrowed for `'1` + | ^^^^^^^^^^^^ `list[_].next` was mutably borrowed here in the previous iteration of the loop +LL | list[0] = n; + | ----------- assignment requires that `list[_].next` is borrowed for `'1` error: aborting due to 2 previous errors diff --git a/tests/ui/nll/issue-62007-assign-differing-fields.stderr b/tests/ui/nll/issue-62007-assign-differing-fields.stderr index f1af2e855afe..d51fd7a1389a 100644 --- a/tests/ui/nll/issue-62007-assign-differing-fields.stderr +++ b/tests/ui/nll/issue-62007-assign-differing-fields.stderr @@ -17,10 +17,9 @@ LL | fn to_refs<'a, T>(mut list: (&'a mut List, &'a mut List)) -> Vec<&'a | -- lifetime `'a` defined here ... LL | if let Some(n) = (list.0).next.as_mut() { - | ^^^^^^^^^^^^^--------- - | | - | `list.0.next` was mutably borrowed here in the previous iteration of the loop - | argument requires that `list.0.next` is borrowed for `'a` + | ^^^^^^^^^^^^^ `list.0.next` was mutably borrowed here in the previous iteration of the loop +LL | list.1 = n; + | ---------- assignment requires that `list.0.next` is borrowed for `'a` error: aborting due to 2 previous errors diff --git a/tests/ui/nll/issue-67007-escaping-data.rs b/tests/ui/nll/issue-67007-escaping-data.rs index 49ea2e5964fa..92a47f788430 100644 --- a/tests/ui/nll/issue-67007-escaping-data.rs +++ b/tests/ui/nll/issue-67007-escaping-data.rs @@ -12,8 +12,8 @@ struct Consumer<'tcx>(&'tcx ()); impl<'tcx> Consumer<'tcx> { fn bad_method<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) { - let other = self.use_fcx(fcx); //~ ERROR lifetime may not live long enough - fcx.use_it(other); + let other = self.use_fcx(fcx); + fcx.use_it(other); //~ ERROR lifetime may not live long enough } fn use_fcx<'a>(&self, _: &FnCtxt<'a, 'tcx>) -> &'a () { diff --git a/tests/ui/nll/issue-67007-escaping-data.stderr b/tests/ui/nll/issue-67007-escaping-data.stderr index eb7b57c7e998..3b9ed2468518 100644 --- a/tests/ui/nll/issue-67007-escaping-data.stderr +++ b/tests/ui/nll/issue-67007-escaping-data.stderr @@ -1,14 +1,18 @@ error: lifetime may not live long enough - --> $DIR/issue-67007-escaping-data.rs:15:21 + --> $DIR/issue-67007-escaping-data.rs:16:9 | LL | impl<'tcx> Consumer<'tcx> { | ---- lifetime `'tcx` defined here LL | fn bad_method<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) { | -- lifetime `'a` defined here LL | let other = self.use_fcx(fcx); - | ^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'tcx` +LL | fcx.use_it(other); + | ^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'tcx` | = help: consider adding the following bound: `'a: 'tcx` + = note: requirement occurs because of the type `FnCtxt<'_, '_>`, which makes the generic argument `'_` invariant + = note: the struct `FnCtxt<'a, 'tcx>` is invariant over the parameter `'tcx` + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/nll/issue-95272.rs b/tests/ui/nll/issue-95272.rs index 958cbde37882..b3325a05e5c0 100644 --- a/tests/ui/nll/issue-95272.rs +++ b/tests/ui/nll/issue-95272.rs @@ -8,8 +8,8 @@ where fn test<'a, 'b>(x: Cell<&'a ()>, y: Cell<&'b ()>) { let f = check; - //~^ ERROR lifetime may not live long enough f(x, y); + //~^ ERROR lifetime may not live long enough } fn main() {} diff --git a/tests/ui/nll/issue-95272.stderr b/tests/ui/nll/issue-95272.stderr index 0453ef8e53ea..3d1720239e9a 100644 --- a/tests/ui/nll/issue-95272.stderr +++ b/tests/ui/nll/issue-95272.stderr @@ -1,16 +1,17 @@ error: lifetime may not live long enough - --> $DIR/issue-95272.rs:10:13 + --> $DIR/issue-95272.rs:11:5 | LL | fn test<'a, 'b>(x: Cell<&'a ()>, y: Cell<&'b ()>) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here LL | let f = check; - | ^^^^^ assignment requires that `'a` must outlive `'b` +LL | f(x, y); + | ^^^^^^^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` - = note: requirement occurs because of a function pointer to `check` - = note: the function `check` is invariant over the parameter `'a` + = note: requirement occurs because of the type `Cell<&()>`, which makes the generic argument `&()` invariant + = note: the struct `Cell` is invariant over the parameter `T` = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/nll/issue-98589-closures-relate-named-regions.stderr b/tests/ui/nll/issue-98589-closures-relate-named-regions.stderr index 4e741abc2dcf..4f244b54bd07 100644 --- a/tests/ui/nll/issue-98589-closures-relate-named-regions.stderr +++ b/tests/ui/nll/issue-98589-closures-relate-named-regions.stderr @@ -11,14 +11,14 @@ LL | || { None::<&'a &'b ()>; }; = help: consider adding the following bound: `'b: 'a` error: lifetime may not live long enough - --> $DIR/issue-98589-closures-relate-named-regions.rs:15:10 + --> $DIR/issue-98589-closures-relate-named-regions.rs:15:5 | LL | fn test_early_late<'a: 'a, 'b>() { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here LL | || { None::<&'a &'b ()>; }; - | ^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` diff --git a/tests/ui/nll/outlives-suggestion-simple.stderr b/tests/ui/nll/outlives-suggestion-simple.stderr index bcffd575aed4..669532005b29 100644 --- a/tests/ui/nll/outlives-suggestion-simple.stderr +++ b/tests/ui/nll/outlives-suggestion-simple.stderr @@ -99,6 +99,10 @@ LL | fn get_bar(&self) -> Bar2 { | - let's call the lifetime of this reference `'1` LL | Bar2::new(&self) | ^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a` + | + = note: requirement occurs because of the type `Foo2<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Foo2<'a>` is invariant over the parameter `'a` + = help: see for more information about variance error: aborting due to 9 previous errors diff --git a/tests/ui/nll/polonius/assignment-to-differing-field.stderr b/tests/ui/nll/polonius/assignment-to-differing-field.stderr index acac47eac4f0..c46d010e4f57 100644 --- a/tests/ui/nll/polonius/assignment-to-differing-field.stderr +++ b/tests/ui/nll/polonius/assignment-to-differing-field.stderr @@ -17,10 +17,10 @@ LL | fn assignment_to_field_projection<'a, T>( | -- lifetime `'a` defined here ... LL | if let Some(n) = (list.0).next.as_mut() { - | ^^^^^^^^^^^^^--------- - | | - | `list.0.next` was mutably borrowed here in the previous iteration of the loop - | argument requires that `list.0.next` is borrowed for `'a` + | ^^^^^^^^^^^^^ `list.0.next` was mutably borrowed here in the previous iteration of the loop +LL | +LL | list.1 = n; + | ---------- assignment requires that `list.0.next` is borrowed for `'a` error[E0499]: cannot borrow `list.0.0.0.0.0.value` as mutable more than once at a time --> $DIR/assignment-to-differing-field.rs:37:21 @@ -41,10 +41,10 @@ LL | fn assignment_through_projection_chain<'a, T>( | -- lifetime `'a` defined here ... LL | if let Some(n) = ((((list.0).0).0).0).0.next.as_mut() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^--------- - | | - | `list.0.0.0.0.0.next` was mutably borrowed here in the previous iteration of the loop - | argument requires that `list.0.0.0.0.0.next` is borrowed for `'a` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `list.0.0.0.0.0.next` was mutably borrowed here in the previous iteration of the loop +LL | +LL | *((((list.0).0).0).0).1 = n; + | --------------------------- assignment requires that `list.0.0.0.0.0.next` is borrowed for `'a` error: aborting due to 4 previous errors diff --git a/tests/ui/nll/relate_tys/var-appears-twice.stderr b/tests/ui/nll/relate_tys/var-appears-twice.stderr index 3f9a6cec0d2e..2b2ec88ba8e0 100644 --- a/tests/ui/nll/relate_tys/var-appears-twice.stderr +++ b/tests/ui/nll/relate_tys/var-appears-twice.stderr @@ -5,9 +5,10 @@ LL | let b = 44; | - binding `b` declared here ... LL | let x: DoubleCell<_> = make_cell(&b); - | ------------- ^^ borrowed value does not live long enough - | | - | type annotation requires that `b` is borrowed for `'static` + | ----------^^- + | | | + | | borrowed value does not live long enough + | assignment requires that `b` is borrowed for `'static` ... LL | } | - `b` dropped here while still borrowed diff --git a/tests/ui/nll/ty-outlives/projection-one-region-closure.stderr b/tests/ui/nll/ty-outlives/projection-one-region-closure.stderr index dda60398198e..408c57a31eea 100644 --- a/tests/ui/nll/ty-outlives/projection-one-region-closure.stderr +++ b/tests/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -24,6 +24,22 @@ LL | | T: Anything<'b>, | = note: defining type: no_relationships_late::<'?1, T> +error: lifetime may not live long enough + --> $DIR/projection-one-region-closure.rs:45:5 + | +LL | fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of the type `Cell<&'?6 ()>`, which makes the generic argument `&'?6 ()` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance + error[E0309]: the parameter type `T` may not live long enough --> $DIR/projection-one-region-closure.rs:45:39 | @@ -38,19 +54,6 @@ help: consider adding an explicit lifetime bound LL | T: Anything<'b> + 'a, | ++++ -error: lifetime may not live long enough - --> $DIR/projection-one-region-closure.rs:45:39 - | -LL | fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` - | - = help: consider adding the following bound: `'b: 'a` - note: external requirements --> $DIR/projection-one-region-closure.rs:56:29 | @@ -77,6 +80,22 @@ LL | | 'a: 'a, | = note: defining type: no_relationships_early::<'?1, '?2, T> +error: lifetime may not live long enough + --> $DIR/projection-one-region-closure.rs:56:5 + | +LL | fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of the type `Cell<&'?7 ()>`, which makes the generic argument `&'?7 ()` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance + error[E0309]: the parameter type `T` may not live long enough --> $DIR/projection-one-region-closure.rs:56:39 | @@ -91,19 +110,6 @@ help: consider adding an explicit lifetime bound LL | T: Anything<'b> + 'a, | ++++ -error: lifetime may not live long enough - --> $DIR/projection-one-region-closure.rs:56:39 - | -LL | fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` - | - = help: consider adding the following bound: `'b: 'a` - note: external requirements --> $DIR/projection-one-region-closure.rs:70:29 | diff --git a/tests/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr b/tests/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr index 52040663e005..4ebdf10c5bf3 100644 --- a/tests/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr +++ b/tests/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr @@ -24,7 +24,7 @@ LL | | T: Anything<'b>, = note: defining type: no_relationships_late::<'?1, T> error: lifetime may not live long enough - --> $DIR/projection-one-region-trait-bound-closure.rs:37:39 + --> $DIR/projection-one-region-trait-bound-closure.rs:37:5 | LL | fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) | -- -- lifetime `'b` defined here @@ -32,9 +32,12 @@ LL | fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) | lifetime `'a` defined here ... LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of the type `Cell<&'?6 ()>`, which makes the generic argument `&'?6 ()` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance note: external requirements --> $DIR/projection-one-region-trait-bound-closure.rs:47:29 @@ -62,7 +65,7 @@ LL | | 'a: 'a, = note: defining type: no_relationships_early::<'?1, '?2, T> error: lifetime may not live long enough - --> $DIR/projection-one-region-trait-bound-closure.rs:47:39 + --> $DIR/projection-one-region-trait-bound-closure.rs:47:5 | LL | fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) | -- -- lifetime `'b` defined here @@ -70,9 +73,12 @@ LL | fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) | lifetime `'a` defined here ... LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of the type `Cell<&'?7 ()>`, which makes the generic argument `&'?7 ()` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance note: external requirements --> $DIR/projection-one-region-trait-bound-closure.rs:60:29 diff --git a/tests/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/tests/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr index c157e89ff8f3..f8dbe08af616 100644 --- a/tests/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr +++ b/tests/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr @@ -182,7 +182,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of the type `Cell<&'?8 ()>`, which makes the generic argument `&'?8 ()` invariant + = note: requirement occurs because of the type `Cell<&'?6 ()>`, which makes the generic argument `&'?6 ()` invariant = note: the struct `Cell` is invariant over the parameter `T` = help: see for more information about variance diff --git a/tests/ui/nll/type-check-pointer-comparisons.stderr b/tests/ui/nll/type-check-pointer-comparisons.stderr index 37098b585dfe..e362dfb3c6e7 100644 --- a/tests/ui/nll/type-check-pointer-comparisons.stderr +++ b/tests/ui/nll/type-check-pointer-comparisons.stderr @@ -6,7 +6,7 @@ LL | fn compare_const<'a, 'b>(x: *const &mut &'a i32, y: *const &mut &'b i32) { | | | lifetime `'a` defined here LL | x == y; - | ^ requires that `'a` must outlive `'b` + | ^^^^^^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of a mutable reference to `&i32` @@ -14,14 +14,14 @@ LL | x == y; = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/type-check-pointer-comparisons.rs:4:10 + --> $DIR/type-check-pointer-comparisons.rs:4:5 | LL | fn compare_const<'a, 'b>(x: *const &mut &'a i32, y: *const &mut &'b i32) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here LL | x == y; - | ^ requires that `'b` must outlive `'a` + | ^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a mutable reference to `&i32` @@ -38,7 +38,7 @@ LL | fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) { | | | lifetime `'a` defined here LL | x == y; - | ^ requires that `'a` must outlive `'b` + | ^^^^^^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of a mutable pointer to `&i32` @@ -46,14 +46,14 @@ LL | x == y; = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/type-check-pointer-comparisons.rs:10:10 + --> $DIR/type-check-pointer-comparisons.rs:10:5 | LL | fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here LL | x == y; - | ^ requires that `'b` must outlive `'a` + | ^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a mutable pointer to `&i32` @@ -72,7 +72,7 @@ LL | fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32 | | | lifetime `'a` defined here LL | f == g; - | ^ requires that `'a` must outlive `'b` + | ^^^^^^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of a mutable reference to `&i32` @@ -80,14 +80,14 @@ LL | f == g; = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/type-check-pointer-comparisons.rs:16:10 + --> $DIR/type-check-pointer-comparisons.rs:16:5 | LL | fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32)) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here LL | f == g; - | ^ requires that `'b` must outlive `'a` + | ^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a mutable reference to `&i32` diff --git a/tests/ui/nll/user-annotations/adt-nullary-enums.stderr b/tests/ui/nll/user-annotations/adt-nullary-enums.stderr index 644fc94f7308..cdaa934122ce 100644 --- a/tests/ui/nll/user-annotations/adt-nullary-enums.stderr +++ b/tests/ui/nll/user-annotations/adt-nullary-enums.stderr @@ -1,52 +1,49 @@ error[E0597]: `c` does not live long enough --> $DIR/adt-nullary-enums.rs:33:41 | -LL | let c = 66; - | - binding `c` declared here -LL | combine( -LL | SomeEnum::SomeVariant(Cell::new(&c)), - | ----------^^- - | | | - | | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'static` -... -LL | } - | - `c` dropped here while still borrowed +LL | let c = 66; + | - binding `c` declared here +LL | / combine( +LL | | SomeEnum::SomeVariant(Cell::new(&c)), + | | ^^ borrowed value does not live long enough +LL | | SomeEnum::SomeOtherVariant::>, +LL | | ); + | |_____- argument requires that `c` is borrowed for `'static` +LL | } + | - `c` dropped here while still borrowed error[E0597]: `c` does not live long enough --> $DIR/adt-nullary-enums.rs:41:41 | -LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { - | -- lifetime `'a` defined here -LL | let c = 66; - | - binding `c` declared here -LL | combine( -LL | SomeEnum::SomeVariant(Cell::new(&c)), - | ----------^^- - | | | - | | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` -... -LL | } - | - `c` dropped here while still borrowed +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +LL | let c = 66; + | - binding `c` declared here +LL | / combine( +LL | | SomeEnum::SomeVariant(Cell::new(&c)), + | | ^^ borrowed value does not live long enough +LL | | SomeEnum::SomeOtherVariant::>, +LL | | ); + | |_____- argument requires that `c` is borrowed for `'a` +LL | } + | - `c` dropped here while still borrowed error[E0597]: `c` does not live long enough --> $DIR/adt-nullary-enums.rs:54:45 | -LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { - | -- lifetime `'a` defined here -LL | let _closure = || { -LL | let c = 66; - | - binding `c` declared here -LL | combine( -LL | SomeEnum::SomeVariant(Cell::new(&c)), - | ----------^^- - | | | - | | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` -... -LL | }; - | - `c` dropped here while still borrowed +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +LL | let _closure = || { +LL | let c = 66; + | - binding `c` declared here +LL | / combine( +LL | | SomeEnum::SomeVariant(Cell::new(&c)), + | | ^^ borrowed value does not live long enough +LL | | SomeEnum::SomeOtherVariant::>, +LL | | ); + | |_________- argument requires that `c` is borrowed for `'a` +LL | }; + | - `c` dropped here while still borrowed error: aborting due to 3 previous errors diff --git a/tests/ui/nll/user-annotations/adt-tuple-struct-calls.stderr b/tests/ui/nll/user-annotations/adt-tuple-struct-calls.stderr index 2084697e7e26..1478ad1431ba 100644 --- a/tests/ui/nll/user-annotations/adt-tuple-struct-calls.stderr +++ b/tests/ui/nll/user-annotations/adt-tuple-struct-calls.stderr @@ -4,11 +4,9 @@ error[E0597]: `c` does not live long enough LL | let c = 66; | - binding `c` declared here LL | let f = SomeStruct::<&'static u32>; + | -------------------------- assignment requires that `c` is borrowed for `'static` LL | f(&c); - | --^^- - | | | - | | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'static` + | ^^ borrowed value does not live long enough LL | } | - `c` dropped here while still borrowed @@ -20,11 +18,9 @@ LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { LL | let c = 66; | - binding `c` declared here LL | let f = SomeStruct::<&'a u32>; + | --------------------- assignment requires that `c` is borrowed for `'a` LL | f(&c); - | --^^- - | | | - | | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` + | ^^ borrowed value does not live long enough LL | } | - `c` dropped here while still borrowed @@ -37,11 +33,9 @@ LL | let _closure = || { LL | let c = 66; | - binding `c` declared here LL | let f = SomeStruct::<&'a u32>; + | --------------------- assignment requires that `c` is borrowed for `'a` LL | f(&c); - | --^^- - | | | - | | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` + | ^^ borrowed value does not live long enough LL | }; | - `c` dropped here while still borrowed diff --git a/tests/ui/nll/user-annotations/method-ufcs-1.stderr b/tests/ui/nll/user-annotations/method-ufcs-1.stderr index c42ea0172cf7..087e270c70f7 100644 --- a/tests/ui/nll/user-annotations/method-ufcs-1.stderr +++ b/tests/ui/nll/user-annotations/method-ufcs-1.stderr @@ -4,11 +4,10 @@ error[E0597]: `a` does not live long enough LL | let a = 22; | - binding `a` declared here ... +LL | let x = <&'static u32 as Bazoom<_>>::method; + | ----------------------------------- assignment requires that `a` is borrowed for `'static` LL | x(&a, b, c); - | --^^------- - | | | - | | borrowed value does not live long enough - | argument requires that `a` is borrowed for `'static` + | ^^ borrowed value does not live long enough LL | } | - `a` dropped here while still borrowed diff --git a/tests/ui/nll/user-annotations/method-ufcs-2.stderr b/tests/ui/nll/user-annotations/method-ufcs-2.stderr index 287337c7d52d..c89bed3b1b18 100644 --- a/tests/ui/nll/user-annotations/method-ufcs-2.stderr +++ b/tests/ui/nll/user-annotations/method-ufcs-2.stderr @@ -4,11 +4,10 @@ error[E0597]: `a` does not live long enough LL | let a = 22; | - binding `a` declared here ... +LL | let x = <&'static u32 as Bazoom<_>>::method; + | ----------------------------------- assignment requires that `a` is borrowed for `'static` LL | x(&a, b, c); - | --^^------- - | | | - | | borrowed value does not live long enough - | argument requires that `a` is borrowed for `'static` + | ^^ borrowed value does not live long enough LL | } | - `a` dropped here while still borrowed diff --git a/tests/ui/nll/where_clauses_in_structs.stderr b/tests/ui/nll/where_clauses_in_structs.stderr index 4cc7e5ab1b4c..19a1ce00e9a8 100644 --- a/tests/ui/nll/where_clauses_in_structs.stderr +++ b/tests/ui/nll/where_clauses_in_structs.stderr @@ -1,12 +1,12 @@ error: lifetime may not live long enough - --> $DIR/where_clauses_in_structs.rs:11:11 + --> $DIR/where_clauses_in_structs.rs:11:14 | LL | fn bar<'a, 'b>(x: Cell<&'a u32>, y: Cell<&'b u32>) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here LL | Foo { x, y }; - | ^ this usage requires that `'a` must outlive `'b` + | ^ this usage requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of the type `Cell<&u32>`, which makes the generic argument `&u32` invariant diff --git a/tests/ui/no-capture-arc.stderr b/tests/ui/no-capture-arc.stderr index 38432c851c52..4a51ddb67a39 100644 --- a/tests/ui/no-capture-arc.stderr +++ b/tests/ui/no-capture-arc.stderr @@ -1,5 +1,5 @@ error[E0382]: borrow of moved value: `arc_v` - --> $DIR/no-capture-arc.rs:14:16 + --> $DIR/no-capture-arc.rs:14:18 | LL | let arc_v = Arc::new(v); | ----- move occurs because `arc_v` has type `Arc>`, which does not implement the `Copy` trait @@ -10,7 +10,7 @@ LL | assert_eq!((*arc_v)[3], 4); | ----- variable moved due to use in closure ... LL | assert_eq!((*arc_v)[2], 3); - | ^^^^^^^^ value borrowed here after move + | ^^^^^ value borrowed here after move | = note: borrow occurs due to deref coercion to `Vec` diff --git a/tests/ui/no-reuse-move-arc.stderr b/tests/ui/no-reuse-move-arc.stderr index cdeb6eadc174..61f4837dc0e6 100644 --- a/tests/ui/no-reuse-move-arc.stderr +++ b/tests/ui/no-reuse-move-arc.stderr @@ -1,5 +1,5 @@ error[E0382]: borrow of moved value: `arc_v` - --> $DIR/no-reuse-move-arc.rs:12:16 + --> $DIR/no-reuse-move-arc.rs:12:18 | LL | let arc_v = Arc::new(v); | ----- move occurs because `arc_v` has type `Arc>`, which does not implement the `Copy` trait @@ -10,7 +10,7 @@ LL | assert_eq!((*arc_v)[3], 4); | ----- variable moved due to use in closure ... LL | assert_eq!((*arc_v)[2], 3); - | ^^^^^^^^ value borrowed here after move + | ^^^^^ value borrowed here after move | = note: borrow occurs due to deref coercion to `Vec` diff --git a/tests/ui/proc-macro/bad-projection.stderr b/tests/ui/proc-macro/bad-projection.stderr index 2e8668f60de7..aa6b3da6da99 100644 --- a/tests/ui/proc-macro/bad-projection.stderr +++ b/tests/ui/proc-macro/bad-projection.stderr @@ -48,16 +48,17 @@ LL | trait Project { | ^^^^^^^^^^^^^ error[E0277]: the trait bound `(): Project` is not satisfied - --> $DIR/bad-projection.rs:14:40 + --> $DIR/bad-projection.rs:14:17 | LL | pub fn uwu() -> <() as Project>::Assoc {} - | ^^ the trait `Project` is not implemented for `()` + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Project` is not implemented for `()` | help: this trait has no implementations, consider adding one --> $DIR/bad-projection.rs:9:1 | LL | trait Project { | ^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 5 previous errors diff --git a/tests/ui/regions/better-blame-constraint-for-outlives-static.rs b/tests/ui/regions/better-blame-constraint-for-outlives-static.rs new file mode 100644 index 000000000000..77cadb783016 --- /dev/null +++ b/tests/ui/regions/better-blame-constraint-for-outlives-static.rs @@ -0,0 +1,13 @@ +//! diagnostic test for #132749: ensure we pick a decent span and reason to blame for region errors +//! when failing to prove a region outlives 'static + +struct Bytes(&'static [u8]); + +fn deserialize_simple_string(buf: &[u8]) -> (Bytes, &[u8]) { + //~^ NOTE let's call the lifetime of this reference `'1` + let (s, rest) = buf.split_at(2); + (Bytes(s), rest) //~ ERROR lifetime may not live long enough + //~| NOTE this usage requires that `'1` must outlive `'static` +} + +fn main() {} diff --git a/tests/ui/regions/better-blame-constraint-for-outlives-static.stderr b/tests/ui/regions/better-blame-constraint-for-outlives-static.stderr new file mode 100644 index 000000000000..e1e88829f2c4 --- /dev/null +++ b/tests/ui/regions/better-blame-constraint-for-outlives-static.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/better-blame-constraint-for-outlives-static.rs:9:12 + | +LL | fn deserialize_simple_string(buf: &[u8]) -> (Bytes, &[u8]) { + | - let's call the lifetime of this reference `'1` +... +LL | (Bytes(s), rest) + | ^ this usage requires that `'1` must outlive `'static` + +error: aborting due to 1 previous error + diff --git a/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr index fcd0a232a7bd..9f1315070eb2 100644 --- a/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr +++ b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr @@ -25,6 +25,10 @@ LL | self.enter_scope(|ctx| { | has type `&mut FooImpl<'2, '_, T>` LL | BarImpl(ctx); | ^^^ this usage requires that `'1` must outlive `'2` + | + = note: requirement occurs because of a mutable reference to `FooImpl<'_, '_, T>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: lifetime may not live long enough --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:22:9 diff --git a/tests/ui/regions/region-invariant-static-error-reporting.rs b/tests/ui/regions/region-invariant-static-error-reporting.rs index 9ab46a775789..e58eea3f61ae 100644 --- a/tests/ui/regions/region-invariant-static-error-reporting.rs +++ b/tests/ui/regions/region-invariant-static-error-reporting.rs @@ -3,7 +3,7 @@ // over time, but this test used to exhibit some pretty bogus messages // that were not remotely helpful. -//@ error-pattern:argument requires that `'a` must outlive `'static` +//@ error-pattern:requires that `'a` must outlive `'static` struct Invariant<'a>(Option<&'a mut &'a mut ()>); @@ -11,9 +11,9 @@ fn mk_static() -> Invariant<'static> { Invariant(None) } fn unify<'a>(x: Option>, f: fn(Invariant<'a>)) { let bad = if x.is_some() { - x.unwrap() //~ ERROR borrowed data escapes outside of function [E0521] + x.unwrap() } else { - mk_static() + mk_static() //~ ERROR lifetime may not live long enough }; f(bad); } diff --git a/tests/ui/regions/region-invariant-static-error-reporting.stderr b/tests/ui/regions/region-invariant-static-error-reporting.stderr index 834d5c6cf5a8..2ccf36713ae8 100644 --- a/tests/ui/regions/region-invariant-static-error-reporting.stderr +++ b/tests/ui/regions/region-invariant-static-error-reporting.stderr @@ -1,16 +1,11 @@ -error[E0521]: borrowed data escapes outside of function - --> $DIR/region-invariant-static-error-reporting.rs:14:9 +error: lifetime may not live long enough + --> $DIR/region-invariant-static-error-reporting.rs:16:9 | LL | fn unify<'a>(x: Option>, f: fn(Invariant<'a>)) { - | -- - `x` is a reference that is only valid in the function body - | | - | lifetime `'a` defined here -LL | let bad = if x.is_some() { -LL | x.unwrap() - | ^^^^^^^^^^ - | | - | `x` escapes the function body here - | argument requires that `'a` must outlive `'static` + | -- lifetime `'a` defined here +... +LL | mk_static() + | ^^^^^^^^^^^ assignment requires that `'a` must outlive `'static` | = note: requirement occurs because of the type `Invariant<'_>`, which makes the generic argument `'_` invariant = note: the struct `Invariant<'a>` is invariant over the parameter `'a` @@ -18,4 +13,3 @@ LL | x.unwrap() error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.rs b/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.rs index 4d77a551f647..1106352037a0 100644 --- a/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.rs +++ b/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.rs @@ -20,8 +20,8 @@ trait Trait2<'a, 'b> { // do not infer that. fn callee<'x, 'y, T>(t: &'x dyn for<'z> Trait1< >::Foo >) //~^ ERROR the trait bound `for<'z> T: Trait2<'y, 'z>` is not satisfied + //~| ERROR the trait bound `for<'z> T: Trait2<'y, 'z>` is not satisfied { - //~^ ERROR the trait bound `for<'z> T: Trait2<'y, 'z>` is not satisfied } fn main() { } diff --git a/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.stderr b/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.stderr index 87f0f47f2401..643746ed46fc 100644 --- a/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.stderr +++ b/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.stderr @@ -10,12 +10,10 @@ LL | fn callee<'x, 'y, T: for<'z> Trait2<'y, 'z>>(t: &'x dyn for<'z> Trait1< T: Trait2<'y, 'z>` is not satisfied - --> $DIR/regions-implied-bounds-projection-gap-hr-1.rs:23:1 + --> $DIR/regions-implied-bounds-projection-gap-hr-1.rs:21:25 | -LL | / { -LL | | -LL | | } - | |_^ the trait `for<'z> Trait2<'y, 'z>` is not implemented for `T` +LL | fn callee<'x, 'y, T>(t: &'x dyn for<'z> Trait1< >::Foo >) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'z> Trait2<'y, 'z>` is not implemented for `T` | help: consider restricting type parameter `T` with trait `Trait2` | diff --git a/tests/ui/repr/issue-83505-repr-simd.rs b/tests/ui/repr/issue-83505-repr-simd.rs index 280b771d0153..bebbc636728f 100644 --- a/tests/ui/repr/issue-83505-repr-simd.rs +++ b/tests/ui/repr/issue-83505-repr-simd.rs @@ -5,6 +5,8 @@ #[repr(simd)] //~^ ERROR: attribute should be applied to a struct [E0517] //~| ERROR: unsupported representation for zero-variant enum [E0084] +//~| ERROR: SIMD types are experimental and possibly buggy [E0658] + enum Es {} static CLs: Es; //~^ ERROR: free static item without body diff --git a/tests/ui/repr/issue-83505-repr-simd.stderr b/tests/ui/repr/issue-83505-repr-simd.stderr index df99baaf5229..44e154b4bb61 100644 --- a/tests/ui/repr/issue-83505-repr-simd.stderr +++ b/tests/ui/repr/issue-83505-repr-simd.stderr @@ -1,11 +1,21 @@ error: free static item without body - --> $DIR/issue-83505-repr-simd.rs:9:1 + --> $DIR/issue-83505-repr-simd.rs:11:1 | LL | static CLs: Es; | ^^^^^^^^^^^^^^- | | | help: provide a definition for the static: `= ;` +error[E0658]: SIMD types are experimental and possibly buggy + --> $DIR/issue-83505-repr-simd.rs:5:1 + | +LL | #[repr(simd)] + | ^^^^^^^^^^^^^ + | + = note: see issue #27731 for more information + = help: add `#![feature(repr_simd)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0517]: attribute should be applied to a struct --> $DIR/issue-83505-repr-simd.rs:5:8 | @@ -24,7 +34,7 @@ LL | #[repr(simd)] LL | enum Es {} | ------- zero-variant enum -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0084, E0517. +Some errors have detailed explanations: E0084, E0517, E0658. For more information about an error, try `rustc --explain E0084`. diff --git a/tests/ui/specialization/min_specialization/issue-79224.stderr b/tests/ui/specialization/min_specialization/issue-79224.stderr index b2118ccd81b6..2ed614f1857d 100644 --- a/tests/ui/specialization/min_specialization/issue-79224.stderr +++ b/tests/ui/specialization/min_specialization/issue-79224.stderr @@ -35,13 +35,10 @@ LL | impl Display for Cow<'_, B> { | +++++++++++++++++++ error[E0277]: the trait bound `B: Clone` is not satisfied - --> $DIR/issue-79224.rs:30:62 + --> $DIR/issue-79224.rs:30:12 | -LL | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - | ______________________________________________________________^ -... | -LL | | } - | |_____^ the trait `Clone` is not implemented for `B` +LL | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + | ^^^^^ the trait `Clone` is not implemented for `B` | = note: required for `B` to implement `ToOwned` help: consider further restricting type parameter `B` with trait `Clone` diff --git a/tests/ui/symbol-names/normalize-in-param-env.rs b/tests/ui/symbol-names/normalize-in-param-env.rs new file mode 100644 index 000000000000..a1453eb13eff --- /dev/null +++ b/tests/ui/symbol-names/normalize-in-param-env.rs @@ -0,0 +1,38 @@ +//@ revisions: legacy v0 +//@[v0] compile-flags: -C symbol-mangling-version=v0 +//@[legacy] compile-flags: -C symbol-mangling-version=legacy -Zunstable-options +//@ build-pass + +pub struct Vec2; + +pub trait Point { + type S; +} +impl Point for Vec2 { + type S = f32; +} + +pub trait Point2: Point { + type S2; +} +impl Point2 for Vec2 { + type S2 = Self::S; +} + +trait MyFrom { + fn my_from(); +} +impl MyFrom for P { + fn my_from() { + // This is just a really dumb way to force the legacy symbol mangling to + // mangle the closure's parent impl def path *with* args. Otherwise, + // legacy symbol mangling will strip the args from the instance, meaning + // that we don't trigger the bug. + let c = || {}; + let x = Box::new(c) as Box; + } +} + +fn main() { + >::my_from(); +} diff --git a/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr b/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr index d5d7d7aa627b..b6c3ccdedfb0 100644 --- a/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr +++ b/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr @@ -1,4 +1,4 @@ -warning: target feature `neon` cannot be toggled with `-Ctarget-feature`: unsound on hard-float targets because it changes float ABI +warning: target feature `neon` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs index b3171d52c510..dab01179c0b3 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs @@ -1,11 +1,11 @@ -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -#![feature(no_core, lang_items)] +//@ compile-flags: --target=riscv32e-unknown-none-elf --crate-type=lib +//@ needs-llvm-components: riscv +#![feature(no_core, lang_items, riscv_target_feature)] #![no_core] #[lang = "sized"] pub trait Sized {} -#[target_feature(enable = "x87")] -//~^ERROR: cannot be toggled with +#[target_feature(enable = "d")] +//~^ERROR: cannot be enabled with pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr index 3ebbe69d8aec..9df56d86729c 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr @@ -1,8 +1,8 @@ -error: target feature `x87` cannot be toggled with `#[target_feature]`: unsound on hard-float targets because it changes float ABI +error: target feature `d` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI --> $DIR/forbidden-hardfloat-target-feature-attribute.rs:9:18 | -LL | #[target_feature(enable = "x87")] - | ^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "d")] + | ^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs new file mode 100644 index 000000000000..3d09217327ab --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs @@ -0,0 +1,10 @@ +//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib +//@ needs-llvm-components: x86 +//@ compile-flags: -Ctarget-feature=-sse +// For now this is just a warning. +//@ build-pass +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr new file mode 100644 index 000000000000..72b2d03fe203 --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr @@ -0,0 +1,7 @@ +warning: target feature `sse` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI + | + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116344 + +warning: 1 warning emitted + diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr index d5d7d7aa627b..b6c3ccdedfb0 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr @@ -1,4 +1,4 @@ -warning: target feature `neon` cannot be toggled with `-Ctarget-feature`: unsound on hard-float targets because it changes float ABI +warning: target feature `neon` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr index 604ad2f991ae..6191681286a3 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr @@ -1,7 +1,11 @@ -warning: target feature `x87` cannot be toggled with `-Ctarget-feature`: unsound on hard-float targets because it changes float ABI +warning: unstable feature specified for `-Ctarget-feature`: `x87` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: target feature `x87` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 -warning: 1 warning emitted +warning: 2 warnings emitted diff --git a/tests/ui/target-feature/forbidden-target-feature-attribute.rs b/tests/ui/target-feature/forbidden-target-feature-attribute.rs index f13cdd17da63..2ba5f2215c7b 100644 --- a/tests/ui/target-feature/forbidden-target-feature-attribute.rs +++ b/tests/ui/target-feature/forbidden-target-feature-attribute.rs @@ -7,5 +7,5 @@ pub trait Sized {} #[target_feature(enable = "soft-float")] -//~^ERROR: cannot be toggled with +//~^ERROR: cannot be enabled with pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/forbidden-target-feature-attribute.stderr b/tests/ui/target-feature/forbidden-target-feature-attribute.stderr index 27ac4aaf9605..f3d54cc19d3e 100644 --- a/tests/ui/target-feature/forbidden-target-feature-attribute.stderr +++ b/tests/ui/target-feature/forbidden-target-feature-attribute.stderr @@ -1,4 +1,4 @@ -error: target feature `soft-float` cannot be toggled with `#[target_feature]`: unsound because it changes float ABI +error: target feature `soft-float` cannot be enabled with `#[target_feature]`: unsound because it changes float ABI --> $DIR/forbidden-target-feature-attribute.rs:9:18 | LL | #[target_feature(enable = "soft-float")] diff --git a/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr b/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr index 508e1fe0cf47..797cd4be5c27 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr +++ b/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr @@ -1,4 +1,4 @@ -warning: target feature `soft-float` cannot be toggled with `-Ctarget-feature`: unsound because it changes float ABI +warning: target feature `soft-float` cannot be disabled with `-Ctarget-feature`: unsound because it changes float ABI | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/forbidden-target-feature-flag.stderr b/tests/ui/target-feature/forbidden-target-feature-flag.stderr index 508e1fe0cf47..075666fbf668 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag.stderr +++ b/tests/ui/target-feature/forbidden-target-feature-flag.stderr @@ -1,4 +1,4 @@ -warning: target feature `soft-float` cannot be toggled with `-Ctarget-feature`: unsound because it changes float ABI +warning: target feature `soft-float` cannot be enabled with `-Ctarget-feature`: unsound because it changes float ABI | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/traits/coercion-generic-regions.stderr b/tests/ui/traits/coercion-generic-regions.stderr index 576035f8c13e..c48767095dff 100644 --- a/tests/ui/traits/coercion-generic-regions.stderr +++ b/tests/ui/traits/coercion-generic-regions.stderr @@ -4,11 +4,9 @@ error[E0597]: `person` does not live long enough LL | let person = "Fred".to_string(); | ------ binding `person` declared here LL | let person: &str = &person; - | ^^^^^^^ - | | - | borrowed value does not live long enough - | assignment requires that `person` is borrowed for `'static` + | ^^^^^^^ borrowed value does not live long enough LL | let s: Box> = Box::new(Struct { person: person }); + | ------ this usage requires that `person` is borrowed for `'static` LL | } | - `person` dropped here while still borrowed diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr index 03da9159bea0..4cd87002e491 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr @@ -5,7 +5,7 @@ LL | T::Assoc::::func(); | ^^^^^^^^^^^^^ error[E0277]: the trait bound `U: ~const Other` is not satisfied - --> $DIR/assoc-type-const-bound-usage-fail-2.rs:27:5 + --> $DIR/assoc-type-const-bound-usage-fail-2.rs:26:5 | LL | ::Assoc::::func(); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr index ce58b486a16e..4cd87002e491 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `::Assoc: ~const Trait` is not satisfied +error[E0277]: the trait bound `U: ~const Other` is not satisfied --> $DIR/assoc-type-const-bound-usage-fail-2.rs:24:5 | LL | T::Assoc::::func(); | ^^^^^^^^^^^^^ -error[E0277]: the trait bound `::Assoc: ~const Trait` is not satisfied - --> $DIR/assoc-type-const-bound-usage-fail-2.rs:27:5 +error[E0277]: the trait bound `U: ~const Other` is not satisfied + --> $DIR/assoc-type-const-bound-usage-fail-2.rs:26:5 | LL | ::Assoc::::func(); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs index bdd98eaf541f..e1c30b536112 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs @@ -22,11 +22,9 @@ trait Other {} const fn fails() { T::Assoc::::func(); - //[current]~^ ERROR the trait bound `U: ~const Other` is not satisfied - //[next]~^^ ERROR the trait bound `::Assoc: ~const Trait` is not satisfied + //~^ ERROR the trait bound `U: ~const Other` is not satisfied ::Assoc::::func(); - //[current]~^ ERROR the trait bound `U: ~const Other` is not satisfied - //[next]~^^ ERROR the trait bound `::Assoc: ~const Trait` is not satisfied + //~^ ERROR the trait bound `U: ~const Other` is not satisfied } const fn works() { diff --git a/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.rs b/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.rs new file mode 100644 index 000000000000..f4b01efe9590 --- /dev/null +++ b/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.rs @@ -0,0 +1,22 @@ +// Make sure we don't issue *two* error messages for the trait predicate *and* host predicate. + +#![feature(const_trait_impl)] + +#[const_trait] +trait Trait { + type Out; +} + +const fn needs_const(_: &T) {} + +const IN_CONST: () = { + needs_const(&()); + //~^ ERROR the trait bound `(): Trait` is not satisfied +}; + +const fn conditionally_const() { + needs_const(&()); + //~^ ERROR the trait bound `(): Trait` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.stderr b/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.stderr new file mode 100644 index 000000000000..cd68cdaf8a2b --- /dev/null +++ b/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.stderr @@ -0,0 +1,41 @@ +error[E0277]: the trait bound `(): Trait` is not satisfied + --> $DIR/double-error-for-unimplemented-trait.rs:13:15 + | +LL | needs_const(&()); + | ----------- ^^^ the trait `Trait` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/double-error-for-unimplemented-trait.rs:6:1 + | +LL | trait Trait { + | ^^^^^^^^^^^ +note: required by a bound in `needs_const` + --> $DIR/double-error-for-unimplemented-trait.rs:10:25 + | +LL | const fn needs_const(_: &T) {} + | ^^^^^^^^^^^^ required by this bound in `needs_const` + +error[E0277]: the trait bound `(): Trait` is not satisfied + --> $DIR/double-error-for-unimplemented-trait.rs:18:15 + | +LL | needs_const(&()); + | ----------- ^^^ the trait `Trait` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/double-error-for-unimplemented-trait.rs:6:1 + | +LL | trait Trait { + | ^^^^^^^^^^^ +note: required by a bound in `needs_const` + --> $DIR/double-error-for-unimplemented-trait.rs:10:25 + | +LL | const fn needs_const(_: &T) {} + | ^^^^^^^^^^^^ required by this bound in `needs_const` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr b/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr index 3fc6f5847094..0d53bc5897ef 100644 --- a/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr +++ b/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr @@ -16,6 +16,11 @@ error[E0277]: the trait bound `T: ~const Bar` is not satisfied LL | type Assoc = C | ^^^^ | +note: required for `C` to implement `~const Bar` + --> $DIR/item-bound-entailment-fails.rs:14:15 + | +LL | impl const Bar for C where T: ~const Bar {} + | ^^^ ^^^^ ------ unsatisfied trait bound introduced here note: required by a bound in `Foo::Assoc` --> $DIR/item-bound-entailment-fails.rs:5:20 | diff --git a/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.rs b/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.rs new file mode 100644 index 000000000000..129e90a07f43 --- /dev/null +++ b/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.rs @@ -0,0 +1,17 @@ +//@ compile-flags: -Znext-solver + +trait Trait<'a> { + type Assoc; +} + +fn foo(x: for<'a> fn(<() as Trait<'a>>::Assoc)) { + //~^ ERROR the trait bound `for<'a> (): Trait<'a>` is not satisfied + //~| ERROR the trait bound `for<'a> (): Trait<'a>` is not satisfied + //~| ERROR the trait bound `for<'a> (): Trait<'a>` is not satisfied + unsafe { std::mem::transmute::<_, ()>(x); } + //~^ ERROR the trait bound `for<'a> (): Trait<'a>` is not satisfied + //~| ERROR the trait bound `for<'a> (): Trait<'a>` is not satisfied + //~| ERROR the trait bound `for<'a> (): Trait<'a>` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.stderr b/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.stderr new file mode 100644 index 000000000000..2d42fedae438 --- /dev/null +++ b/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.stderr @@ -0,0 +1,75 @@ +error[E0277]: the trait bound `for<'a> (): Trait<'a>` is not satisfied + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:7:11 + | +LL | fn foo(x: for<'a> fn(<() as Trait<'a>>::Assoc)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Trait<'a>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:3:1 + | +LL | trait Trait<'a> { + | ^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `for<'a> (): Trait<'a>` is not satisfied + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:7:8 + | +LL | fn foo(x: for<'a> fn(<() as Trait<'a>>::Assoc)) { + | ^ the trait `for<'a> Trait<'a>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:3:1 + | +LL | trait Trait<'a> { + | ^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `for<'a> (): Trait<'a>` is not satisfied + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:11:14 + | +LL | unsafe { std::mem::transmute::<_, ()>(x); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Trait<'a>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:3:1 + | +LL | trait Trait<'a> { + | ^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `for<'a> (): Trait<'a>` is not satisfied + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:11:36 + | +LL | unsafe { std::mem::transmute::<_, ()>(x); } + | ^ the trait `for<'a> Trait<'a>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:3:1 + | +LL | trait Trait<'a> { + | ^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `for<'a> (): Trait<'a>` is not satisfied + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:11:43 + | +LL | unsafe { std::mem::transmute::<_, ()>(x); } + | ^ the trait `for<'a> Trait<'a>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:3:1 + | +LL | trait Trait<'a> { + | ^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `for<'a> (): Trait<'a>` is not satisfied + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:7:1 + | +LL | fn foo(x: for<'a> fn(<() as Trait<'a>>::Assoc)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Trait<'a>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:3:1 + | +LL | trait Trait<'a> { + | ^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-3.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-3.stderr index 0a969b611e9d..01b8da645f31 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-3.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-3.stderr @@ -1,10 +1,10 @@ error: lifetime may not live long enough - --> $DIR/type-checking-test-3.rs:11:18 + --> $DIR/type-checking-test-3.rs:11:13 | LL | fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) { | -- lifetime `'a` defined here LL | let _ = x as &dyn Bar<'a>; // Error - | ^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static` error: lifetime may not live long enough --> $DIR/type-checking-test-3.rs:16:18 diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr index 090120a2327a..e91ea193a010 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr @@ -1,10 +1,10 @@ error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:19:18 + --> $DIR/type-checking-test-4.rs:19:13 | LL | fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) { | -- lifetime `'a` defined here LL | let _ = x as &dyn Bar<'static, 'a>; // Error - | ^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static` error: lifetime may not live long enough --> $DIR/type-checking-test-4.rs:24:18 diff --git a/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr b/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr index 98f99cdbfbd6..c24f8fd867fb 100644 --- a/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr +++ b/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr @@ -15,21 +15,17 @@ LL | fn underconstrain(_: T) -> Underconstrained { | +++++++ error[E0277]: the trait bound `T: Trait` is not satisfied - --> $DIR/generic_underconstrained.rs:9:51 + --> $DIR/generic_underconstrained.rs:9:31 | -LL | fn underconstrain(_: T) -> Underconstrained { - | ___________________________________________________^ -LL | | -LL | | -LL | | unimplemented!() -LL | | } - | |_^ the trait `Trait` is not implemented for `T` +LL | fn underconstrain(_: T) -> Underconstrained { + | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` | note: required by a bound on the type alias `Underconstrained` --> $DIR/generic_underconstrained.rs:6:26 | LL | type Underconstrained = impl Send; | ^^^^^ required by this bound + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider restricting type parameter `T` with trait `Trait` | LL | fn underconstrain(_: T) -> Underconstrained { diff --git a/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr b/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr index 5506977a3e70..93df5ddca796 100644 --- a/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr +++ b/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr @@ -31,42 +31,34 @@ LL | fn underconstrained2(_: U, _: V) -> Underconstrained | +++++++++++++++++ error[E0277]: `U` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:8:53 + --> $DIR/generic_underconstrained2.rs:8:33 | -LL | fn underconstrained(_: U) -> Underconstrained { - | _____________________________________________________^ -LL | | -LL | | -LL | | 5u32 -LL | | } - | |_^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` +LL | fn underconstrained(_: U) -> Underconstrained { + | ^^^^^^^^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` | note: required by a bound on the type alias `Underconstrained` --> $DIR/generic_underconstrained2.rs:5:26 | LL | type Underconstrained = impl Send; | ^^^^^^^^^^^^^^^ required by this bound + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider restricting type parameter `U` with trait `Debug` | LL | fn underconstrained(_: U) -> Underconstrained { | +++++++++++++++++ error[E0277]: `V` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:17:64 + --> $DIR/generic_underconstrained2.rs:17:43 | -LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { - | ________________________________________________________________^ -LL | | -LL | | -LL | | 5u32 -LL | | } - | |_^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` +LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { + | ^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` | note: required by a bound on the type alias `Underconstrained2` --> $DIR/generic_underconstrained2.rs:14:27 | LL | type Underconstrained2 = impl Send; | ^^^^^^^^^^^^^^^ required by this bound + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider restricting type parameter `V` with trait `Debug` | LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr similarity index 81% rename from tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr rename to tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr index 8017e5446cc7..1bcc0dbaf672 100644 --- a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr @@ -1,11 +1,11 @@ error[E0308]: mismatched types - --> $DIR/const-in-impl-fn-return-type.rs:15:39 + --> $DIR/const-in-impl-fn-return-type.rs:20:39 | LL | fn func() -> [(); { () }] { | ^^ expected `usize`, found `()` error: the constant `N` is not of type `usize` - --> $DIR/const-in-impl-fn-return-type.rs:7:32 + --> $DIR/const-in-impl-fn-return-type.rs:12:32 | LL | fn func() -> [(); N]; | ^^^^^^^ expected `usize`, found `u32` diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr new file mode 100644 index 000000000000..1bcc0dbaf672 --- /dev/null +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/const-in-impl-fn-return-type.rs:20:39 + | +LL | fn func() -> [(); { () }] { + | ^^ expected `usize`, found `()` + +error: the constant `N` is not of type `usize` + --> $DIR/const-in-impl-fn-return-type.rs:12:32 + | +LL | fn func() -> [(); N]; + | ^^^^^^^ expected `usize`, found `u32` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs index 5eef26887211..6bbac9d45bbe 100644 --- a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs @@ -1,4 +1,9 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + // Regression test for #114918 + // Test that a const generic enclosed in a block within the return type // of an impl fn produces a type mismatch error instead of triggering // a const eval cycle diff --git a/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr b/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr index 40d341402455..e2f48f37f0da 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr @@ -7,6 +7,10 @@ LL | doit(0, &|x, y| { | has type `&Cell<&'2 i32>` LL | x.set(y); | ^^^^^^^^ argument requires that `'1` must outlive `'2` + | + = note: requirement occurs because of the type `Cell<&i32>`, which makes the generic argument `&i32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.stderr b/tests/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.stderr index ed9d22d25583..8bd9b1112c56 100644 --- a/tests/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.stderr +++ b/tests/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.stderr @@ -7,6 +7,9 @@ LL | fn foo(x: &mut Vec<&'_ u8>, y: &'_ u8) { x.push(y); } | | let's call the lifetime of this reference `'1` | let's call the lifetime of this reference `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } diff --git a/tests/ui/variance/variance-associated-types2.stderr b/tests/ui/variance/variance-associated-types2.stderr index 158b09b0630c..292d60941b18 100644 --- a/tests/ui/variance/variance-associated-types2.stderr +++ b/tests/ui/variance/variance-associated-types2.stderr @@ -1,10 +1,10 @@ error: lifetime may not live long enough - --> $DIR/variance-associated-types2.rs:13:12 + --> $DIR/variance-associated-types2.rs:13:42 | LL | fn take<'a>(_: &'a u32) { | -- lifetime `'a` defined here LL | let _: Box> = make(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^ coercion requires that `'a` must outlive `'static` error: aborting due to 1 previous error diff --git a/triagebot.toml b/triagebot.toml index 436c88541a5f..7241b448c487 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -172,6 +172,12 @@ issues). """ label = "O-emscripten" +[ping.relnotes-interest-group] +message = """\ +Hi relnotes-interest-group, this PR adds release notes. Could you review this PR +if you have time? Thanks <3 +""" + [prioritize] label = "I-prioritize" @@ -498,6 +504,11 @@ trigger_files = [ "src/tools/compiletest" ] +[autolabel."A-rustc-dev-guide"] +trigger_files = [ + "src/doc/rustc-dev-guide", +] + [notify-zulip."I-prioritize"] zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts topic = "#{number} {title}" @@ -819,7 +830,7 @@ cc = ["@rust-lang/rustfmt"] [mentions."compiler/rustc_middle/src/mir/syntax.rs"] message = "This PR changes MIR" -cc = ["@oli-obk", "@RalfJung", "@JakobDegen", "@davidtwco", "@celinval", "@vakaras"] +cc = ["@oli-obk", "@RalfJung", "@JakobDegen", "@davidtwco", "@vakaras"] [mentions."compiler/rustc_error_messages"] message = "`rustc_error_messages` was changed" @@ -990,12 +1001,15 @@ https://github.com/rust-lang/reference/blob/HEAD/src/identifiers.md. """ cc = ["@ehuss"] +[mentions."src/doc/rustc-dev-guide"] +message = "The rustc-dev-guide subtree was changed. If this PR *only* touches the dev guide consider submitting a PR directly to [rust-lang/rustc-dev-guide](https://github.com/rust-lang/rustc-dev-guide/pulls) otherwise thank you for updating the dev guide with your changes." +cc = ["@BoxyUwU", "@jieyouxu", "@kobzol"] + [assign] warn_non_default_branch.enable = true contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html" users_on_vacation = [ "jyn514", - "celinval", "nnethercote", "spastorino", "workingjubilee", @@ -1208,7 +1222,7 @@ project-exploit-mitigations = [ "/src/doc/nomicon" = ["@ehuss"] "/src/doc/reference" = ["@ehuss"] "/src/doc/rust-by-example" = ["@ehuss"] -"/src/doc/rustc-dev-guide" = ["@kobzol", "@jieyouxu"] +"/src/doc/rustc-dev-guide" = ["compiler"] "/src/doc/rustdoc" = ["rustdoc"] "/src/doc/style-guide" = ["style-team"] "/src/etc" = ["@Mark-Simulacrum"]