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/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0e151d25778..c650df6a0ec2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ # and also on pushes to special branches (auto, try). # # The actual definition of the executed jobs is calculated by a Python -# script located at src/ci/github-actions/calculate-job-matrix.py, which +# script located at src/ci/github-actions/ci.py, which # uses job definition data from src/ci/github-actions/jobs.yml. # You should primarily modify the `jobs.yml` file if you want to modify # what jobs are executed in CI. @@ -56,10 +56,10 @@ jobs: - name: Calculate the CI job matrix env: COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - run: python3 src/ci/github-actions/calculate-job-matrix.py >> $GITHUB_OUTPUT + run: python3 src/ci/github-actions/ci.py calculate-job-matrix >> $GITHUB_OUTPUT id: jobs job: - name: ${{ matrix.name }} + name: ${{ matrix.full_name }} needs: [ calculate_matrix ] runs-on: "${{ matrix.os }}" defaults: @@ -67,7 +67,7 @@ jobs: shell: ${{ contains(matrix.os, 'windows') && 'msys2 {0}' || 'bash' }} timeout-minutes: 360 env: - CI_JOB_NAME: ${{ matrix.image }} + CI_JOB_NAME: ${{ matrix.name }} CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse # commit of PR sha or commit sha. `GITHUB_SHA` is not accurate for PRs. HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} @@ -233,7 +233,7 @@ jobs: env: DATADOG_SITE: datadoghq.com DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }} - DD_GITHUB_JOB_NAME: ${{ matrix.name }} + DD_GITHUB_JOB_NAME: ${{ matrix.full_name }} run: | cd src/ci npm ci diff --git a/Cargo.lock b/Cargo.lock index a1365c2d202d..117f1475e000 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" @@ -285,9 +282,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.1" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "regex-automata 0.4.9", @@ -533,7 +530,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -544,7 +541,7 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clippy" -version = "0.1.85" +version = "0.1.86" dependencies = [ "anstream", "cargo_metadata 0.18.1", @@ -564,7 +561,7 @@ dependencies = [ "rustc_tools_util", "serde", "serde_json", - "syn 2.0.93", + "syn 2.0.94", "tempfile", "termize", "tokio", @@ -575,7 +572,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.85" +version = "0.1.86" dependencies = [ "clippy_utils", "itertools", @@ -600,7 +597,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.85" +version = "0.1.86" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -623,7 +620,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.85" +version = "0.1.86" dependencies = [ "arrayvec", "itertools", @@ -674,7 +671,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -899,7 +896,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -910,7 +907,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -947,7 +944,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -968,7 +965,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -978,7 +975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -990,7 +987,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -1068,7 +1065,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -1365,7 +1362,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -1599,7 +1596,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -1788,7 +1785,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -2744,7 +2741,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -3154,7 +3151,7 @@ dependencies = [ "rinja_parser", "rustc-hash 2.1.0", "serde", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -3499,6 +3496,7 @@ name = "rustc_codegen_llvm" version = "0.0.0" dependencies = [ "bitflags", + "gimli 0.30.0", "itertools", "libc", "measureme", @@ -3794,7 +3792,7 @@ dependencies = [ "fluent-syntax", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", "unic-langid", ] @@ -3929,7 +3927,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -4077,7 +4075,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", "synstructure", ] @@ -4117,7 +4115,6 @@ name = "rustc_middle" version = "0.0.0" dependencies = [ "bitflags", - "derive-where", "either", "field-offset", "gsgdt", @@ -4161,6 +4158,7 @@ dependencies = [ "rustc_apfloat", "rustc_arena", "rustc_ast", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -4665,7 +4663,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", "synstructure", ] @@ -4754,7 +4752,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -4891,7 +4889,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -5008,9 +5006,9 @@ dependencies = [ [[package]] name = "spdx" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae30cc7bfe3656d60ee99bf6836f472b0c53dddcbf335e253329abb16e535a2" +checksum = "58b69356da67e2fc1f542c71ea7e654a361a79c938e4424392ecf4fa065d2193" dependencies = [ "smallvec", ] @@ -5149,9 +5147,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.93" +version = "2.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3" dependencies = [ "proc-macro2", "quote", @@ -5166,7 +5164,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -5203,12 +5201,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", @@ -5308,7 +5307,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -5319,7 +5318,7 @@ checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -5520,7 +5519,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -5691,7 +5690,7 @@ checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.93", + "syn 2.0.94", "unic-langid-impl", ] @@ -5897,7 +5896,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", "wasm-bindgen-shared", ] @@ -5919,7 +5918,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6101,7 +6100,7 @@ dependencies = [ "rayon", "serde", "serde_json", - "syn 2.0.93", + "syn 2.0.94", "windows-metadata", ] @@ -6134,7 +6133,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -6145,7 +6144,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -6424,7 +6423,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", "synstructure", ] @@ -6446,7 +6445,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] [[package]] @@ -6466,7 +6465,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", "synstructure", ] @@ -6489,5 +6488,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.94", ] diff --git a/RELEASES.md b/RELEASES.md index 99733bade32c..c4b36ed988bd 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/123723) + + + +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) +- [`Pin::as_deref_mut`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.as_deref_mut) + +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) +- [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) ========================== @@ -2067,7 +2183,7 @@ Language -------- - [Stabilize default_alloc_error_handler](https://github.com/rust-lang/rust/pull/102318/) - This allows usage of `alloc` on stable without requiring the + This allows usage of `alloc` on stable without requiring the definition of a handler for allocation failure. Defining custom handlers is still unstable. - [Stabilize `efiapi` calling convention.](https://github.com/rust-lang/rust/pull/105795/) - [Remove implicit promotion for types with drop glue](https://github.com/rust-lang/rust/pull/105085/) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 226a46f605cc..b8773f9ff38f 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -119,6 +119,8 @@ impl LayoutCalculator { .chain(Niche::from_scalar(dl, Size::ZERO, a)) .max_by_key(|niche| niche.available(dl)); + let combined_seed = a.size(&self.cx).bytes().wrapping_add(b.size(&self.cx).bytes()); + LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, fields: FieldsShape::Arbitrary { @@ -131,6 +133,7 @@ impl LayoutCalculator { size, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed: combined_seed, } } @@ -223,6 +226,7 @@ impl LayoutCalculator { size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: dl.i8_align.abi, + randomization_seed: 0, } } @@ -385,6 +389,11 @@ impl LayoutCalculator { return Err(LayoutCalculatorError::EmptyUnion); }; + let combined_seed = only_variant + .iter() + .map(|v| v.randomization_seed) + .fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed)); + Ok(LayoutData { variants: Variants::Single { index: only_variant_idx }, fields: FieldsShape::Union(union_field_count), @@ -394,6 +403,7 @@ impl LayoutCalculator { size: size.align_to(align.abi), max_repr_align, unadjusted_abi_align, + randomization_seed: combined_seed, }) } @@ -650,6 +660,11 @@ impl LayoutCalculator { BackendRepr::Memory { sized: true } }; + let combined_seed = variant_layouts + .iter() + .map(|v| v.randomization_seed) + .fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed)); + let layout = LayoutData { variants: Variants::Multiple { tag: niche_scalar, @@ -671,6 +686,7 @@ impl LayoutCalculator { align, max_repr_align, unadjusted_abi_align, + randomization_seed: combined_seed, }; Some(TmpLayout { layout, variants: variant_layouts }) @@ -961,6 +977,11 @@ impl LayoutCalculator { let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag); + let combined_seed = layout_variants + .iter() + .map(|v| v.randomization_seed) + .fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed)); + let tagged_layout = LayoutData { variants: Variants::Multiple { tag, @@ -978,6 +999,7 @@ impl LayoutCalculator { size, max_repr_align, unadjusted_abi_align, + randomization_seed: combined_seed, }; let tagged_layout = TmpLayout { layout: tagged_layout, variants: layout_variants }; @@ -1030,12 +1052,15 @@ impl LayoutCalculator { let mut max_repr_align = repr.align; let mut inverse_memory_index: IndexVec = fields.indices().collect(); let optimize_field_order = !repr.inhibit_struct_field_reordering(); - if optimize_field_order && fields.len() > 1 { - let end = - if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; - let optimizing = &mut inverse_memory_index.raw[..end]; - let fields_excluding_tail = &fields.raw[..end]; + let end = if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; + let optimizing = &mut inverse_memory_index.raw[..end]; + let fields_excluding_tail = &fields.raw[..end]; + // unsizable tail fields are excluded so that we use the same seed for the sized and unsized layouts. + let field_seed = fields_excluding_tail + .iter() + .fold(0u64, |acc, f| acc.wrapping_add(f.randomization_seed)); + if optimize_field_order && fields.len() > 1 { // If `-Z randomize-layout` was enabled for the type definition we can shuffle // the field ordering to try and catch some code making assumptions about layouts // we don't guarantee. @@ -1046,8 +1071,9 @@ impl LayoutCalculator { use rand::seq::SliceRandom; // `ReprOptions.field_shuffle_seed` is a deterministic seed we can use to randomize field // ordering. - let mut rng = - rand_xoshiro::Xoshiro128StarStar::seed_from_u64(repr.field_shuffle_seed); + let mut rng = rand_xoshiro::Xoshiro128StarStar::seed_from_u64( + field_seed.wrapping_add(repr.field_shuffle_seed), + ); // Shuffle the ordering of the fields. optimizing.shuffle(&mut rng); @@ -1344,6 +1370,8 @@ impl LayoutCalculator { unadjusted_abi_align }; + let seed = field_seed.wrapping_add(repr.field_shuffle_seed); + Ok(LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, fields: FieldsShape::Arbitrary { offsets, memory_index }, @@ -1353,6 +1381,7 @@ impl LayoutCalculator { size, max_repr_align, unadjusted_abi_align, + randomization_seed: seed, }) } diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 7fa869a509cc..8ad33749f347 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1719,6 +1719,18 @@ pub struct LayoutData { /// Only used on aarch64-linux, where the argument passing ABI ignores the requested alignment /// in some cases. pub unadjusted_abi_align: Align, + + /// The randomization seed based on this type's own repr and its fields. + /// + /// Since randomization is toggled on a per-crate basis even crates that do not have randomization + /// enabled should still calculate a seed so that downstream uses can use it to distinguish different + /// types. + /// + /// For every T and U for which we do not guarantee that a repr(Rust) `Foo` can be coerced or + /// transmuted to `Foo` we aim to create probalistically distinct seeds so that Foo can choose + /// to reorder its fields based on that information. The current implementation is a conservative + /// approximation of this goal. + pub randomization_seed: u64, } impl LayoutData { @@ -1739,6 +1751,30 @@ impl LayoutData { let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar); let size = scalar.size(cx); let align = scalar.align(cx); + + let range = scalar.valid_range(cx); + + // All primitive types for which we don't have subtype coercions should get a distinct seed, + // so that types wrapping them can use randomization to arrive at distinct layouts. + // + // Some type information is already lost at this point, so as an approximation we derive + // the seed from what remains. For example on 64-bit targets usize and u64 can no longer + // be distinguished. + let randomization_seed = size + .bytes() + .wrapping_add( + match scalar.primitive() { + Primitive::Int(_, true) => 1, + Primitive::Int(_, false) => 2, + Primitive::Float(_) => 3, + Primitive::Pointer(_) => 4, + } << 32, + ) + // distinguishes references from pointers + .wrapping_add((range.start as u64).rotate_right(16)) + // distinguishes char from u32 and bool from u8 + .wrapping_add((range.end as u64).rotate_right(16)); + LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, fields: FieldsShape::Primitive, @@ -1748,6 +1784,7 @@ impl LayoutData { align, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed, } } } @@ -1770,6 +1807,7 @@ where variants, max_repr_align, unadjusted_abi_align, + ref randomization_seed, } = self; f.debug_struct("Layout") .field("size", size) @@ -1780,6 +1818,7 @@ where .field("variants", variants) .field("max_repr_align", max_repr_align) .field("unadjusted_abi_align", unadjusted_abi_align) + .field("randomization_seed", randomization_seed) .finish() } } diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 4d8525989cc5..b21ccba93bb4 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -78,7 +78,7 @@ impl ArenaChunk { // been initialized. unsafe { let slice = self.storage.as_mut(); - ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut slice[..len])); + slice[..len].assume_init_drop(); } } } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3a81b93d157c..8e73df63ef54 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -623,7 +623,7 @@ impl Pat { PatKind::Wild | PatKind::Rest | PatKind::Never - | PatKind::Lit(_) + | PatKind::Expr(_) | PatKind::Range(..) | PatKind::Ident(..) | PatKind::Path(..) @@ -801,8 +801,8 @@ pub enum PatKind { /// A reference pattern (e.g., `&mut (a, b)`). Ref(P, Mutability), - /// A literal. - Lit(P), + /// A literal, const block or path. + Expr(P), /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`). Range(Option>, Option>, Spanned), diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 04cdfc93dcb1..aa88e8369d5b 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1512,7 +1512,7 @@ pub fn walk_pat(vis: &mut T, pat: &mut P) { vis.visit_ident(ident); visit_opt(sub, |sub| vis.visit_pat(sub)); } - PatKind::Lit(e) => vis.visit_expr(e), + PatKind::Expr(e) => vis.visit_expr(e), PatKind::TupleStruct(qself, path, elems) => { vis.visit_qself(qself); vis.visit_path(path); diff --git a/compiler/rustc_ast/src/util/comments/tests.rs b/compiler/rustc_ast/src/util/comments/tests.rs index f88b534a0c11..7a87ccea62a7 100644 --- a/compiler/rustc_ast/src/util/comments/tests.rs +++ b/compiler/rustc_ast/src/util/comments/tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] +#![allow(rustc::symbol_intern_string_literal)] use rustc_span::create_default_session_globals_then; diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index e99fc7b604e3..1d6d7330757d 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -680,7 +680,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res try_visit!(visitor.visit_ident(ident)); visit_opt!(visitor, visit_pat, optional_subpattern); } - PatKind::Lit(expression) => try_visit!(visitor.visit_expr(expression)), + PatKind::Expr(expression) => try_visit!(visitor.visit_expr(expression)), PatKind::Range(lower_bound, upper_bound, _end) => { visit_opt!(visitor, visit_expr, lower_bound); visit_opt!(visitor, visit_expr, upper_bound); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index d16a3ce390db..a76ca6772e52 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -102,17 +102,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), - ExprKind::ConstBlock(c) => { - let c = self.with_new_scopes(c.value.span, |this| { - let def_id = this.local_def_id(c.id); - hir::ConstBlock { - def_id, - hir_id: this.lower_node_id(c.id), - body: this.lower_const_body(c.value.span, Some(&c.value)), - } - }); - hir::ExprKind::ConstBlock(c) - } + ExprKind::ConstBlock(c) => hir::ExprKind::ConstBlock(self.lower_const_block(c)), ExprKind::Repeat(expr, count) => { let expr = self.lower_expr(expr); let count = self.lower_array_length_to_const_arg(count); @@ -153,18 +143,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let ohs = self.lower_expr(ohs); hir::ExprKind::Unary(op, ohs) } - ExprKind::Lit(token_lit) => { - let lit_kind = match LitKind::from_token_lit(*token_lit) { - Ok(lit_kind) => lit_kind, - Err(err) => { - let guar = - report_lit_error(&self.tcx.sess.psess, err, *token_lit, e.span); - LitKind::Err(guar) - } - }; - let lit = self.arena.alloc(respan(self.lower_span(e.span), lit_kind)); - hir::ExprKind::Lit(lit) - } + ExprKind::Lit(token_lit) => hir::ExprKind::Lit(self.lower_lit(token_lit, e.span)), ExprKind::IncludedBytes(bytes) => { let lit = self.arena.alloc(respan( self.lower_span(e.span), @@ -403,6 +382,32 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } + pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock { + self.with_new_scopes(c.value.span, |this| { + let def_id = this.local_def_id(c.id); + hir::ConstBlock { + def_id, + hir_id: this.lower_node_id(c.id), + body: this.lower_const_body(c.value.span, Some(&c.value)), + } + }) + } + + pub(crate) fn lower_lit( + &mut self, + token_lit: &token::Lit, + span: Span, + ) -> &'hir Spanned { + let lit_kind = match LitKind::from_token_lit(*token_lit) { + Ok(lit_kind) => lit_kind, + Err(err) => { + let guar = report_lit_error(&self.tcx.sess.psess, err, *token_lit, span); + LitKind::Err(guar) + } + }; + self.arena.alloc(respan(self.lower_span(span), lit_kind)) + } + fn lower_unop(&mut self, u: UnOp) -> hir::UnOp { match u { UnOp::Deref => hir::UnOp::Deref, diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index c3ff7b4b8972..29d4fb9ef259 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -209,6 +209,14 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } + fn visit_pat_expr(&mut self, expr: &'hir PatExpr<'hir>) { + self.insert(expr.span, expr.hir_id, Node::PatExpr(expr)); + + self.with_parent(expr.hir_id, |this| { + intravisit::walk_pat_expr(this, expr); + }); + } + fn visit_pat_field(&mut self, field: &'hir PatField<'hir>) { self.insert(field.span, field.hir_id, Node::PatField(field)); self.with_parent(field.hir_id, |this| { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index fe2d5a594f31..0e28590bd660 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -35,6 +35,7 @@ #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(rustdoc_internals)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index a4ab2561b721..abd314ae74c3 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -1,9 +1,12 @@ +use std::sync::Arc; + use rustc_ast::ptr::P; use rustc_ast::*; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_span::source_map::Spanned; +use rustc_middle::span_bug; +use rustc_span::source_map::{Spanned, respan}; use rustc_span::{Ident, Span}; use super::errors::{ @@ -35,8 +38,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lower_sub, ); } - PatKind::Lit(e) => { - break hir::PatKind::Lit(self.lower_expr_within_pat(e, false)); + PatKind::Expr(e) => { + break hir::PatKind::Expr(self.lower_expr_within_pat(e, false)); } PatKind::TupleStruct(qself, path, pats) => { let qpath = self.lower_qpath( @@ -120,8 +123,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. @@ -366,24 +370,54 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // } // m!(S); // ``` - fn lower_expr_within_pat(&mut self, expr: &Expr, allow_paths: bool) -> &'hir hir::Expr<'hir> { - match &expr.kind { - ExprKind::Lit(..) - | ExprKind::ConstBlock(..) - | ExprKind::IncludedBytes(..) - | ExprKind::Err(_) - | ExprKind::Dummy => {} - ExprKind::Path(..) if allow_paths => {} - ExprKind::Unary(UnOp::Neg, inner) if matches!(inner.kind, ExprKind::Lit(_)) => {} + fn lower_expr_within_pat( + &mut self, + expr: &Expr, + allow_paths: bool, + ) -> &'hir hir::PatExpr<'hir> { + let err = |guar| hir::PatExprKind::Lit { + lit: self.arena.alloc(respan(self.lower_span(expr.span), LitKind::Err(guar))), + negated: false, + }; + let kind = match &expr.kind { + ExprKind::Lit(lit) => { + hir::PatExprKind::Lit { lit: self.lower_lit(lit, expr.span), negated: false } + } + ExprKind::ConstBlock(c) => hir::PatExprKind::ConstBlock(self.lower_const_block(c)), + ExprKind::IncludedBytes(bytes) => hir::PatExprKind::Lit { + lit: self.arena.alloc(respan( + self.lower_span(expr.span), + LitKind::ByteStr(Arc::clone(bytes), StrStyle::Cooked), + )), + negated: false, + }, + ExprKind::Err(guar) => err(*guar), + ExprKind::Dummy => span_bug!(expr.span, "lowered ExprKind::Dummy"), + ExprKind::Path(qself, path) if allow_paths => hir::PatExprKind::Path(self.lower_qpath( + expr.id, + qself, + path, + ParamMode::Optional, + AllowReturnTypeNotation::No, + ImplTraitContext::Disallowed(ImplTraitPosition::Path), + None, + )), + ExprKind::Unary(UnOp::Neg, inner) if let ExprKind::Lit(lit) = &inner.kind => { + hir::PatExprKind::Lit { lit: self.lower_lit(lit, expr.span), negated: true } + } _ => { let pattern_from_macro = expr.is_approximately_pattern(); let guar = self.dcx().emit_err(ArbitraryExpressionInPattern { span: expr.span, pattern_from_macro_note: pattern_from_macro, }); - return self.arena.alloc(self.expr_err(expr.span, guar)); + err(guar) } - } - self.lower_expr(expr) + }; + self.arena.alloc(hir::PatExpr { + hir_id: self.lower_node_id(expr.id), + span: expr.span, + kind, + }) } } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 3fbf12101861..94746212138d 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) { @@ -692,7 +692,7 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) { .find(|feat| feat.gate_name == sym::generic_const_exprs) .map(|feat| feat.attr_sp) { - #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + #[allow(rustc::symbol_intern_string_literal)] sess.dcx().emit_err(errors::IncompatibleFeatures { spans: vec![gce_span], f1: Symbol::intern("-Znext-solver=globally"), diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 172df1029292..9b958ed6b0d6 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1701,7 +1701,7 @@ impl<'a> State<'a> { self.print_pat(inner); } } - PatKind::Lit(e) => self.print_expr(e, FixupContext::default()), + PatKind::Expr(e) => self.print_expr(e, FixupContext::default()), PatKind::Range(begin, end, Spanned { node: end_kind, .. }) => { if let Some(e) = begin { self.print_expr(e, FixupContext::default()); diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 8986bec57de7..def6b16ee8ae 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -11,6 +11,22 @@ pub enum InlineAttr { Hint, Always, Never, + /// `#[rustc_force_inline]` forces inlining to happen in the MIR inliner - it reports an error + /// if the inlining cannot happen. It is limited to only free functions so that the calls + /// can always be resolved. + Force { + attr_span: Span, + reason: Option, + }, +} + +impl InlineAttr { + pub fn always(&self) -> bool { + match self { + InlineAttr::Always | InlineAttr::Force { .. } => true, + InlineAttr::None | InlineAttr::Hint | InlineAttr::Never => false, + } + } } #[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)] 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..303fa469332e 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -2,7 +2,7 @@ use std::fmt; use std::ops::Index; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, Body, Local, Location, traversal}; use rustc_middle::span_bug; @@ -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> { @@ -132,7 +131,7 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> { pub enum LocalsStateAtExit { AllAreInvalidated, - SomeAreInvalidated { has_storage_dead_or_moved: BitSet }, + SomeAreInvalidated { has_storage_dead_or_moved: DenseBitSet }, } impl LocalsStateAtExit { @@ -141,7 +140,7 @@ impl LocalsStateAtExit { body: &Body<'tcx>, move_data: &MoveData<'tcx>, ) -> Self { - struct HasStorageDead(BitSet); + struct HasStorageDead(DenseBitSet); impl<'tcx> Visitor<'tcx> for HasStorageDead { fn visit_local(&mut self, local: Local, ctx: PlaceContext, _: Location) { @@ -154,7 +153,8 @@ impl LocalsStateAtExit { if locals_are_invalidated_at_exit { LocalsStateAtExit::AllAreInvalidated } else { - let mut has_storage_dead = HasStorageDead(BitSet::new_empty(body.local_decls.len())); + let mut has_storage_dead = + HasStorageDead(DenseBitSet::new_empty(body.local_decls.len())); has_storage_dead.visit_body(body); let mut has_storage_dead_or_moved = has_storage_dead.0; for move_out in &move_data.moves { @@ -350,7 +350,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/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 7e8c48a15513..5a89f7c351cf 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -11,8 +11,8 @@ pub use super::dataflow::{BorrowIndex, Borrows, calculate_borrows_out_of_scope_a pub use super::place_ext::PlaceExt; pub use super::places_conflict::{PlaceConflictBias, places_conflict}; pub use super::polonius::legacy::{ - AllFacts as PoloniusInput, LocationTable, PoloniusOutput, PoloniusRegionVid, RichLocation, - RustcFacts, + PoloniusFacts as PoloniusInput, PoloniusLocationTable, PoloniusOutput, PoloniusRegionVid, + RichLocation, RustcFacts, }; pub use super::region_infer::RegionInferenceContext; @@ -33,7 +33,7 @@ pub enum ConsumerOptions { /// without significant slowdowns. /// /// Implies [`RegionInferenceContext`](ConsumerOptions::RegionInferenceContext), - /// and additionally retrieve the [`LocationTable`] and [`PoloniusInput`] that + /// and additionally retrieve the [`PoloniusLocationTable`] and [`PoloniusInput`] that /// would be given to Polonius. Critically, this does not run Polonius, which /// one may want to avoid due to performance issues on large bodies. PoloniusInputFacts, @@ -71,7 +71,7 @@ pub struct BodyWithBorrowckFacts<'tcx> { /// The table that maps Polonius points to locations in the table. /// Populated when using [`ConsumerOptions::PoloniusInputFacts`] /// or [`ConsumerOptions::PoloniusOutputFacts`]. - pub location_table: Option, + pub location_table: Option, /// Polonius input facts. /// Populated when using [`ConsumerOptions::PoloniusInputFacts`] /// or [`ConsumerOptions::PoloniusOutputFacts`]. diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index abe4d4f20ecc..a7a6f2da509c 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -2,7 +2,7 @@ use std::fmt; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{ self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges, }; @@ -180,7 +180,7 @@ pub struct Borrows<'a, 'tcx> { } struct OutOfScopePrecomputer<'a, 'tcx> { - visited: BitSet, + visited: DenseBitSet, visit_stack: Vec, body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>, @@ -190,7 +190,7 @@ struct OutOfScopePrecomputer<'a, 'tcx> { impl<'a, 'tcx> OutOfScopePrecomputer<'a, 'tcx> { fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self { OutOfScopePrecomputer { - visited: BitSet::new_empty(body.basic_blocks.len()), + visited: DenseBitSet::new_empty(body.basic_blocks.len()), visit_stack: vec![], body, regioncx, @@ -292,7 +292,7 @@ pub fn calculate_borrows_out_of_scope_at_location<'tcx>( } struct PoloniusOutOfScopePrecomputer<'a, 'tcx> { - visited: BitSet, + visited: DenseBitSet, visit_stack: Vec, body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>, @@ -303,7 +303,7 @@ struct PoloniusOutOfScopePrecomputer<'a, 'tcx> { impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> { fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self { Self { - visited: BitSet::new_empty(body.basic_blocks.len()), + visited: DenseBitSet::new_empty(body.basic_blocks.len()), visit_stack: vec![], body, regioncx, @@ -559,7 +559,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { } } -type BorrowsDomain = BitSet; +type BorrowsDomain = DenseBitSet; /// Forward dataflow computation of the set of borrows that are in scope at a particular location. /// - we gen the introduced loans @@ -575,7 +575,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { // bottom = nothing is reserved or activated yet; - BitSet::new_empty(self.borrow_set.len()) + DenseBitSet::new_empty(self.borrow_set.len()) } fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 8e5944d6cf45..4d19d89b3ce2 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, @@ -2698,22 +2680,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { return; } - let mut sugg = vec![]; let sm = self.infcx.tcx.sess.source_map(); - - if let Some(span) = finder.closure_arg_span { - sugg.push((sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg)); - } - for span in finder.closure_change_spans { - sugg.push((span, "this".to_string())); - } - - for (span, suggest) in finder.closure_call_changes { - sugg.push((span, suggest)); - } + let sugg = finder + .closure_arg_span + .map(|span| (sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg)) + .into_iter() + .chain( + finder.closure_change_spans.into_iter().map(|span| (span, "this".to_string())), + ) + .chain(finder.closure_call_changes) + .collect(); err.multipart_suggestion_verbose( - "try explicitly pass `&Self` into the Closure as an argument", + "try explicitly passing `&Self` into the closure as an argument", sugg, Applicability::MachineApplicable, ); @@ -3046,15 +3025,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 +3038,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 +3091,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 +3264,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 +3755,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..45764f9c6b86 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,16 +16,19 @@ #![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::bit_set::{DenseBitSet, MixedBitSet}; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::{ InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt, @@ -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}; @@ -56,7 +60,7 @@ use crate::diagnostics::{ use crate::path_utils::*; use crate::place_ext::PlaceExt; use crate::places_conflict::{PlaceConflictBias, places_conflict}; -use crate::polonius::legacy::{LocationTable, PoloniusOutput}; +use crate::polonius::legacy::{PoloniusLocationTable, PoloniusOutput}; use crate::prefixes::PrefixSet; use crate::region_infer::RegionInferenceContext; use crate::renumber::RegionCtxt; @@ -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; @@ -176,7 +179,7 @@ fn do_mir_borrowck<'tcx>( infcx.register_predefined_opaques_for_next_solver(def); } - let location_table = LocationTable::new(body); + let location_table = PoloniusLocationTable::new(body); let move_data = MoveData::gather_moves(body, tcx, |_| true); let promoted_move_data = promoted @@ -247,7 +250,8 @@ fn do_mir_borrowck<'tcx>( infcx: &infcx, body: promoted_body, move_data: &move_data, - location_table: &location_table, // no need to create a real one for the promoted, it is not used + // no need to create a real location table for the promoted, it is not used + location_table: &location_table, movable_coroutine, fn_self_span_reported: Default::default(), locals_are_invalidated_at_exit, @@ -513,7 +517,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { /// Map from MIR `Location` to `LocationIndex`; created /// when MIR borrowck begins. - location_table: &'a LocationTable, + location_table: &'a PoloniusLocationTable, movable_coroutine: bool, /// This keeps track of whether local variables are free-ed when the function @@ -636,9 +640,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 +1013,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } } + fn borrows_in_scope<'s>( + &self, + location: Location, + state: &'s BorrowckDomain, + ) -> Cow<'s, DenseBitSet> { + 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 = DenseBitSet::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 +1042,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 +1067,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 +1107,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { this.buffer_error(err); } } - Control::Break + ControlFlow::Break(()) } (Reservation(kind) | Activation(kind, _) | Write(kind), _) => { @@ -1141,7 +1154,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { this.report_illegal_mutation_of_borrowed(location, place_span, borrow) } } - Control::Break + ControlFlow::Break(()) } }, ); @@ -1149,6 +1162,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 +1253,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..aa0bfd721472 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -28,7 +28,9 @@ use crate::borrow_set::BorrowSet; use crate::consumers::ConsumerOptions; use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors}; use crate::polonius::LocalizedOutlivesConstraintSet; -use crate::polonius::legacy::{AllFacts, AllFactsExt, LocationTable, PoloniusOutput}; +use crate::polonius::legacy::{ + PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput, +}; use crate::region_infer::RegionInferenceContext; use crate::type_check::{self, MirTypeckResults}; use crate::universal_regions::UniversalRegions; @@ -39,7 +41,7 @@ use crate::{BorrowckInferCtxt, polonius, renumber}; pub(crate) struct NllOutput<'tcx> { pub regioncx: RegionInferenceContext<'tcx>, pub opaque_type_values: FxIndexMap>, - pub polonius_input: Option>, + pub polonius_input: Option>, pub polonius_output: Option>, pub opt_closure_req: Option>, pub nll_errors: RegionErrors<'tcx>, @@ -80,7 +82,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( universal_regions: UniversalRegions<'tcx>, body: &Body<'tcx>, promoted: &IndexSlice>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, @@ -91,10 +93,10 @@ pub(crate) fn compute_regions<'a, 'tcx>( || is_polonius_legacy_enabled; let polonius_output = consumer_options.map(|c| c.polonius_output()).unwrap_or_default() || is_polonius_legacy_enabled; - let mut all_facts = - (polonius_input || AllFacts::enabled(infcx.tcx)).then_some(AllFacts::default()); + let mut polonius_facts = + (polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default()); - let elements = Rc::new(DenseLocationMap::new(body)); + let location_map = Rc::new(DenseLocationMap::new(body)); // Run the MIR type-checker. let MirTypeckResults { @@ -109,10 +111,10 @@ pub(crate) fn compute_regions<'a, 'tcx>( universal_regions, location_table, borrow_set, - &mut all_facts, + &mut polonius_facts, flow_inits, move_data, - Rc::clone(&elements), + Rc::clone(&location_map), ); // Create the region inference context, taking ownership of the @@ -122,7 +124,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( // If requested, emit legacy polonius facts. polonius::legacy::emit_facts( - &mut all_facts, + &mut polonius_facts, infcx.tcx, location_table, body, @@ -137,23 +139,23 @@ pub(crate) fn compute_regions<'a, 'tcx>( var_infos, constraints, universal_region_relations, - elements, + location_map, ); // 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| { + let polonius_output = polonius_facts.as_ref().and_then(|polonius_facts| { if infcx.tcx.sess.opts.unstable_opts.nll_facts { let def_id = body.source.def_id(); let def_path = infcx.tcx.def_path(def_id); let dir_path = PathBuf::from(&infcx.tcx.sess.opts.unstable_opts.nll_facts_dir) .join(def_path.to_filename_friendly_no_crate()); - all_facts.write_to_dir(dir_path, location_table).unwrap(); + polonius_facts.write_to_dir(dir_path, location_table).unwrap(); } if polonius_output { @@ -162,7 +164,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( let algorithm = Algorithm::from_str(&algorithm).unwrap(); debug!("compute_regions: using polonius algorithm {:?}", algorithm); let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis"); - Some(Box::new(Output::compute(all_facts, algorithm, false))) + Some(Box::new(Output::compute(polonius_facts, algorithm, false))) } else { None } @@ -182,7 +184,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( NllOutput { regioncx, opaque_type_values: remapped_opaque_tys, - polonius_input: all_facts.map(Box::new), + polonius_input: polonius_facts.map(Box::new), polonius_output, opt_closure_req: closure_region_requirements, nll_errors, 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/accesses.rs b/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs index 4a0c8d9b4b46..edd7ca578b72 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs @@ -4,16 +4,16 @@ use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::move_paths::{LookupResult, MoveData}; use tracing::debug; -use super::{AllFacts, LocationIndex, LocationTable}; +use super::{LocationIndex, PoloniusFacts, PoloniusLocationTable}; use crate::def_use::{self, DefUse}; use crate::universal_regions::UniversalRegions; /// Emit polonius facts for variable defs, uses, drops, and path accesses. pub(crate) fn emit_access_facts<'tcx>( tcx: TyCtxt<'tcx>, - facts: &mut AllFacts, + facts: &mut PoloniusFacts, body: &Body<'tcx>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, move_data: &MoveData<'tcx>, universal_regions: &UniversalRegions<'tcx>, ) { @@ -31,9 +31,9 @@ pub(crate) fn emit_access_facts<'tcx>( /// MIR visitor extracting point-wise facts about accesses. struct AccessFactsExtractor<'a, 'tcx> { - facts: &'a mut AllFacts, + facts: &'a mut PoloniusFacts, move_data: &'a MoveData<'tcx>, - location_table: &'a LocationTable, + location_table: &'a PoloniusLocationTable, } impl<'tcx> AccessFactsExtractor<'_, 'tcx> { diff --git a/compiler/rustc_borrowck/src/polonius/legacy/facts.rs b/compiler/rustc_borrowck/src/polonius/legacy/facts.rs index 42c4e7332183..64389b11a651 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/facts.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/facts.rs @@ -4,13 +4,13 @@ use std::fs::{self, File}; use std::io::Write; use std::path::Path; -use polonius_engine::{AllFacts as PoloniusFacts, Atom, Output}; +use polonius_engine::{AllFacts, Atom, Output}; use rustc_macros::extension; use rustc_middle::mir::Local; use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_mir_dataflow::move_paths::MovePathIndex; -use super::{LocationIndex, LocationTable}; +use super::{LocationIndex, PoloniusLocationTable}; use crate::BorrowIndex; #[derive(Copy, Clone, Debug)] @@ -49,11 +49,11 @@ impl polonius_engine::FactTypes for RustcFacts { type Path = MovePathIndex; } -pub type AllFacts = PoloniusFacts; +pub type PoloniusFacts = AllFacts; -#[extension(pub(crate) trait AllFactsExt)] -impl AllFacts { - /// Returns `true` if there is a need to gather `AllFacts` given the +#[extension(pub(crate) trait PoloniusFactsExt)] +impl PoloniusFacts { + /// Returns `true` if there is a need to gather `PoloniusFacts` given the /// current `-Z` flags. fn enabled(tcx: TyCtxt<'_>) -> bool { tcx.sess.opts.unstable_opts.nll_facts @@ -63,7 +63,7 @@ impl AllFacts { fn write_to_dir( &self, dir: impl AsRef, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, ) -> Result<(), Box> { let dir: &Path = dir.as_ref(); fs::create_dir_all(dir)?; @@ -119,7 +119,7 @@ impl Atom for LocationIndex { } struct FactWriter<'w> { - location_table: &'w LocationTable, + location_table: &'w PoloniusLocationTable, dir: &'w Path, } @@ -141,7 +141,7 @@ trait FactRow { fn write( &self, out: &mut dyn Write, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, ) -> Result<(), Box>; } @@ -149,7 +149,7 @@ impl FactRow for PoloniusRegionVid { fn write( &self, out: &mut dyn Write, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, ) -> Result<(), Box> { write_row(out, location_table, &[self]) } @@ -163,7 +163,7 @@ where fn write( &self, out: &mut dyn Write, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, ) -> Result<(), Box> { write_row(out, location_table, &[&self.0, &self.1]) } @@ -178,7 +178,7 @@ where fn write( &self, out: &mut dyn Write, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, ) -> Result<(), Box> { write_row(out, location_table, &[&self.0, &self.1, &self.2]) } @@ -194,7 +194,7 @@ where fn write( &self, out: &mut dyn Write, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, ) -> Result<(), Box> { write_row(out, location_table, &[&self.0, &self.1, &self.2, &self.3]) } @@ -202,7 +202,7 @@ where fn write_row( out: &mut dyn Write, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, columns: &[&dyn FactCell], ) -> Result<(), Box> { for (index, c) in columns.iter().enumerate() { @@ -213,41 +213,41 @@ fn write_row( } trait FactCell { - fn to_string(&self, location_table: &LocationTable) -> String; + fn to_string(&self, location_table: &PoloniusLocationTable) -> String; } impl FactCell for BorrowIndex { - fn to_string(&self, _location_table: &LocationTable) -> String { + fn to_string(&self, _location_table: &PoloniusLocationTable) -> String { format!("{self:?}") } } impl FactCell for Local { - fn to_string(&self, _location_table: &LocationTable) -> String { + fn to_string(&self, _location_table: &PoloniusLocationTable) -> String { format!("{self:?}") } } impl FactCell for MovePathIndex { - fn to_string(&self, _location_table: &LocationTable) -> String { + fn to_string(&self, _location_table: &PoloniusLocationTable) -> String { format!("{self:?}") } } impl FactCell for PoloniusRegionVid { - fn to_string(&self, _location_table: &LocationTable) -> String { + fn to_string(&self, _location_table: &PoloniusLocationTable) -> String { format!("{self:?}") } } impl FactCell for RegionVid { - fn to_string(&self, _location_table: &LocationTable) -> String { + fn to_string(&self, _location_table: &PoloniusLocationTable) -> String { format!("{self:?}") } } impl FactCell for LocationIndex { - fn to_string(&self, location_table: &LocationTable) -> String { + fn to_string(&self, location_table: &PoloniusLocationTable) -> String { format!("{:?}", location_table.to_rich_location(*self)) } } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index bb6d593d0d88..0ad91ae51a3a 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; @@ -9,7 +11,7 @@ use rustc_middle::mir::{ use rustc_middle::ty::TyCtxt; use tracing::debug; -use super::{AllFacts, LocationTable}; +use super::{PoloniusFacts, PoloniusLocationTable}; use crate::borrow_set::BorrowSet; use crate::path_utils::*; use crate::{ @@ -20,9 +22,9 @@ use crate::{ /// Emit `loan_invalidated_at` facts. pub(super) fn emit_loan_invalidations<'tcx>( tcx: TyCtxt<'tcx>, - facts: &mut AllFacts, + facts: &mut PoloniusFacts, body: &Body<'tcx>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, borrow_set: &BorrowSet<'tcx>, ) { let dominators = body.basic_blocks.dominators(); @@ -33,9 +35,9 @@ pub(super) fn emit_loan_invalidations<'tcx>( struct LoanInvalidationsGenerator<'a, 'tcx> { tcx: TyCtxt<'tcx>, - facts: &'a mut AllFacts, + facts: &'a mut PoloniusFacts, body: &'a Body<'tcx>, - location_table: &'a LocationTable, + location_table: &'a PoloniusLocationTable, dominators: &'a Dominators, borrow_set: &'a BorrowSet<'tcx>, } @@ -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/legacy/loan_kills.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs index 0148e0b28696..098c922bf7b9 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs @@ -6,16 +6,16 @@ use rustc_middle::mir::{ use rustc_middle::ty::TyCtxt; use tracing::debug; -use super::{AllFacts, LocationTable}; +use super::{PoloniusFacts, PoloniusLocationTable}; use crate::borrow_set::BorrowSet; use crate::places_conflict; /// Emit `loan_killed_at` and `cfg_edge` facts at the same time. pub(super) fn emit_loan_kills<'tcx>( tcx: TyCtxt<'tcx>, - facts: &mut AllFacts, + facts: &mut PoloniusFacts, body: &Body<'tcx>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, borrow_set: &BorrowSet<'tcx>, ) { let mut visitor = LoanKillsGenerator { borrow_set, tcx, location_table, facts, body }; @@ -26,8 +26,8 @@ pub(super) fn emit_loan_kills<'tcx>( struct LoanKillsGenerator<'a, 'tcx> { tcx: TyCtxt<'tcx>, - facts: &'a mut AllFacts, - location_table: &'a LocationTable, + facts: &'a mut PoloniusFacts, + location_table: &'a PoloniusLocationTable, borrow_set: &'a BorrowSet<'tcx>, body: &'a Body<'tcx>, } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/location.rs b/compiler/rustc_borrowck/src/polonius/legacy/location.rs index 4cb1202033cb..5f816bb9bbdc 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/location.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/location.rs @@ -13,7 +13,7 @@ use tracing::debug; /// granularity through outlives relations; however, the rich location /// table serves another purpose: it compresses locations from /// multiple words into a single u32. -pub struct LocationTable { +pub struct PoloniusLocationTable { num_points: usize, statements_before_block: IndexVec, } @@ -30,7 +30,7 @@ pub enum RichLocation { Mid(Location), } -impl LocationTable { +impl PoloniusLocationTable { pub(crate) fn new(body: &Body<'_>) -> Self { let mut num_points = 0; let statements_before_block = body @@ -43,8 +43,8 @@ impl LocationTable { }) .collect(); - debug!("LocationTable(statements_before_block={:#?})", statements_before_block); - debug!("LocationTable: num_points={:#?}", num_points); + debug!("PoloniusLocationTable(statements_before_block={:#?})", statements_before_block); + debug!("PoloniusLocationTable: num_points={:#?}", num_points); Self { num_points, statements_before_block } } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs index 45bdbd1e9997..95820c07a02f 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs @@ -36,16 +36,16 @@ pub use self::facts::*; /// /// The rest of the facts are emitted during typeck and liveness. pub(crate) fn emit_facts<'tcx>( - all_facts: &mut Option, + facts: &mut Option, tcx: TyCtxt<'tcx>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, move_data: &MoveData<'tcx>, universal_region_relations: &UniversalRegionRelations<'tcx>, constraints: &MirTypeckRegionConstraints<'tcx>, ) { - let Some(facts) = all_facts else { + let Some(facts) = facts else { // We don't do anything if there are no facts to fill. return; }; @@ -67,9 +67,9 @@ pub(crate) fn emit_facts<'tcx>( /// Emit facts needed for move/init analysis: moves and assignments. fn emit_move_facts( - facts: &mut AllFacts, + facts: &mut PoloniusFacts, body: &Body<'_>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, move_data: &MoveData<'_>, ) { facts.path_is_var.extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l))); @@ -139,7 +139,7 @@ fn emit_move_facts( /// Emit universal regions facts, and their relations. fn emit_universal_region_facts( - facts: &mut AllFacts, + facts: &mut PoloniusFacts, borrow_set: &BorrowSet<'_>, universal_region_relations: &UniversalRegionRelations<'_>, ) { @@ -187,10 +187,10 @@ pub(crate) fn emit_drop_facts<'tcx>( local: Local, kind: &GenericArg<'tcx>, universal_regions: &UniversalRegions<'tcx>, - all_facts: &mut Option, + facts: &mut Option, ) { debug!("emit_drop_facts(local={:?}, kind={:?}", local, kind); - let Some(facts) = all_facts.as_mut() else { return }; + let Some(facts) = facts.as_mut() else { return }; let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); tcx.for_each_free_region(kind, |drop_live_region| { let region_vid = universal_regions.to_region_vid(drop_live_region); @@ -201,8 +201,8 @@ pub(crate) fn emit_drop_facts<'tcx>( /// Emit facts about the outlives constraints: the `subset` base relation, i.e. not a transitive /// closure. fn emit_outlives_facts<'tcx>( - facts: &mut AllFacts, - location_table: &LocationTable, + facts: &mut PoloniusFacts, + location_table: &PoloniusLocationTable, constraints: &MirTypeckRegionConstraints<'tcx>, ) { facts.subset_base.extend(constraints.outlives_constraints.outlives().iter().flat_map( 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..c177538ee177 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; @@ -396,7 +392,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { var_infos: VarInfos, constraints: MirTypeckRegionConstraints<'tcx>, universal_region_relations: Frozen>, - elements: Rc, + location_map: Rc, ) -> Self { let universal_regions = &universal_region_relations.universal_regions; let MirTypeckRegionConstraints { @@ -440,7 +436,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } let mut scc_values = - RegionValues::new(elements, universal_regions.len(), placeholder_indices); + RegionValues::new(location_map, universal_regions.len(), placeholder_indices); for region in liveness_constraints.regions() { let scc = constraint_sccs.scc(region); @@ -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/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 3e16a3ca157d..7c484327e311 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -152,7 +152,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { let (Ok(e) | Err(e)) = prev .build_mismatch_error( &OpaqueHiddenType { ty, span: concrete_type.span }, - opaque_type_key.def_id, infcx.tcx, ) .map(|d| d.emit()); diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index e567f3a8b0de..11fb125ca228 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -38,7 +38,7 @@ pub(crate) enum RegionElement { /// an interval matrix storing liveness ranges for each region-vid. pub(crate) struct LivenessValues { /// The map from locations to points. - elements: Rc, + location_map: Rc, /// Which regions are live. This is exclusive with the fine-grained tracking in `points`, and /// currently only used for validating promoteds (which don't care about more precise tracking). @@ -77,11 +77,11 @@ impl LiveLoans { impl LivenessValues { /// Create an empty map of regions to locations where they're live. - pub(crate) fn with_specific_points(elements: Rc) -> Self { + pub(crate) fn with_specific_points(location_map: Rc) -> Self { LivenessValues { live_regions: None, - points: Some(SparseIntervalMatrix::new(elements.num_points())), - elements, + points: Some(SparseIntervalMatrix::new(location_map.num_points())), + location_map, loans: None, } } @@ -90,11 +90,11 @@ impl LivenessValues { /// /// Unlike `with_specific_points`, does not track exact locations where something is live, only /// which regions are live. - pub(crate) fn without_specific_points(elements: Rc) -> Self { + pub(crate) fn without_specific_points(location_map: Rc) -> Self { LivenessValues { live_regions: Some(Default::default()), points: None, - elements, + location_map, loans: None, } } @@ -122,11 +122,11 @@ impl LivenessValues { /// Records `region` as being live at the given `location`. pub(crate) fn add_location(&mut self, region: RegionVid, location: Location) { - let point = self.elements.point_from_location(location); + let point = self.location_map.point_from_location(location); debug!("LivenessValues::add_location(region={:?}, location={:?})", region, location); if let Some(points) = &mut self.points { points.insert(region, point); - } else if self.elements.point_in_range(point) { + } else if self.location_map.point_in_range(point) { self.live_regions.as_mut().unwrap().insert(region); } @@ -143,7 +143,7 @@ impl LivenessValues { debug!("LivenessValues::add_points(region={:?}, points={:?})", region, points); if let Some(this) = &mut self.points { this.union_row(region, points); - } else if points.iter().any(|point| self.elements.point_in_range(point)) { + } else if points.iter().any(|point| self.location_map.point_in_range(point)) { self.live_regions.as_mut().unwrap().insert(region); } @@ -170,7 +170,7 @@ impl LivenessValues { /// Returns whether `region` is marked live at the given `location`. pub(crate) fn is_live_at(&self, region: RegionVid, location: Location) -> bool { - let point = self.elements.point_from_location(location); + let point = self.location_map.point_from_location(location); if let Some(points) = &self.points { points.row(region).is_some_and(|r| r.contains(point)) } else { @@ -191,25 +191,26 @@ impl LivenessValues { .row(region) .into_iter() .flat_map(|set| set.iter()) - .take_while(|&p| self.elements.point_in_range(p)) + .take_while(|&p| self.location_map.point_in_range(p)) } /// For debugging purposes, returns a pretty-printed string of the points where the `region` is /// live. pub(crate) fn pretty_print_live_points(&self, region: RegionVid) -> String { pretty_print_region_elements( - self.live_points(region).map(|p| RegionElement::Location(self.elements.to_location(p))), + self.live_points(region) + .map(|p| RegionElement::Location(self.location_map.to_location(p))), ) } #[inline] pub(crate) fn point_from_location(&self, location: Location) -> PointIndex { - self.elements.point_from_location(location) + self.location_map.point_from_location(location) } #[inline] pub(crate) fn location_from_point(&self, point: PointIndex) -> Location { - self.elements.to_location(point) + self.location_map.to_location(point) } /// When using `-Zpolonius=next`, returns whether the `loan_idx` is live at the given `point`. @@ -272,7 +273,7 @@ impl PlaceholderIndices { /// because (since it is returned) it must live for at least `'a`. But /// it would also contain various points from within the function. pub(crate) struct RegionValues { - elements: Rc, + location_map: Rc, placeholder_indices: PlaceholderIndices, points: SparseIntervalMatrix, free_regions: SparseBitMatrix, @@ -287,14 +288,14 @@ impl RegionValues { /// Each of the regions in num_region_variables will be initialized with an /// empty set of points and no causal information. pub(crate) fn new( - elements: Rc, + location_map: Rc, num_universal_regions: usize, placeholder_indices: PlaceholderIndices, ) -> Self { - let num_points = elements.num_points(); + let num_points = location_map.num_points(); let num_placeholders = placeholder_indices.len(); Self { - elements, + location_map, points: SparseIntervalMatrix::new(num_points), placeholder_indices, free_regions: SparseBitMatrix::new(num_universal_regions), @@ -336,7 +337,7 @@ impl RegionValues { end: usize, ) -> Option { let row = self.points.row(r)?; - let block = self.elements.entry_point(block); + let block = self.location_map.entry_point(block); let start = block.plus(start); let end = block.plus(end); let first_unset = row.first_unset_in(start..=end)?; @@ -375,8 +376,8 @@ impl RegionValues { pub(crate) fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator + 'a { self.points.row(r).into_iter().flat_map(move |set| { set.iter() - .take_while(move |&p| self.elements.point_in_range(p)) - .map(move |p| self.elements.to_location(p)) + .take_while(move |&p| self.location_map.point_in_range(p)) + .map(move |p| self.location_map.to_location(p)) }) } @@ -430,12 +431,12 @@ pub(crate) trait ToElementIndex: Debug + Copy { impl ToElementIndex for Location { fn add_to_row(self, values: &mut RegionValues, row: N) -> bool { - let index = values.elements.point_from_location(self); + let index = values.location_map.point_from_location(self); values.points.insert(row, index) } fn contained_in_row(self, values: &RegionValues, row: N) -> bool { - let index = values.elements.point_from_location(self); + let index = values.location_map.point_from_location(self); values.points.contains(row, index) } } @@ -464,14 +465,14 @@ impl ToElementIndex for ty::PlaceholderRegion { /// For debugging purposes, returns a pretty-printed string of the given points. pub(crate) fn pretty_print_points( - elements: &DenseLocationMap, + location_map: &DenseLocationMap, points: impl IntoIterator, ) -> String { pretty_print_region_elements( points .into_iter() - .take_while(|&p| elements.point_in_range(p)) - .map(|p| elements.to_location(p)) + .take_while(|&p| location_map.point_in_range(p)) + .map(|p| location_map.to_location(p)) .map(RegionElement::Location), ) } @@ -544,12 +545,12 @@ fn pretty_print_region_elements(elements: impl IntoIterator Iterator for AppearancesIter<'a> { impl LocalUseMap { pub(crate) fn build( live_locals: &[Local], - elements: &DenseLocationMap, + location_map: &DenseLocationMap, body: &Body<'_>, ) -> Self { let nones = IndexVec::from_elem(None, &body.local_decls); @@ -101,7 +101,7 @@ impl LocalUseMap { IndexVec::from_elem(false, &body.local_decls); live_locals.iter().for_each(|&local| locals_with_use_data[local] = true); - LocalUseMapBuild { local_use_map: &mut local_use_map, elements, locals_with_use_data } + LocalUseMapBuild { local_use_map: &mut local_use_map, location_map, locals_with_use_data } .visit_body(body); local_use_map @@ -125,7 +125,7 @@ impl LocalUseMap { struct LocalUseMapBuild<'me> { local_use_map: &'me mut LocalUseMap, - elements: &'me DenseLocationMap, + location_map: &'me DenseLocationMap, // Vector used in `visit_local` to signal which `Local`s do we need // def/use/drop information on, constructed from `live_locals` (that @@ -147,7 +147,7 @@ impl Visitor<'_> for LocalUseMapBuild<'_> { DefUse::Use => &mut self.local_use_map.first_use_at[local], DefUse::Drop => &mut self.local_use_map.first_drop_at[local], }; - let point_index = self.elements.point_from_location(location); + let point_index = self.location_map.point_from_location(location); let appearance_index = self .local_use_map .appearances diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 3e9900cce5f5..f23602d03588 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -32,7 +32,7 @@ mod trace; pub(super) fn generate<'a, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, body: &Body<'tcx>, - elements: &DenseLocationMap, + location_map: &DenseLocationMap, flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, ) { @@ -49,7 +49,7 @@ pub(super) fn generate<'a, 'tcx>( trace::trace( typeck, body, - elements, + location_map, flow_inits, move_data, relevant_live_locals, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index c7a2d32b31dc..4c0d3138f2d3 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -1,5 +1,5 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::IntervalSet; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::for_liveness; @@ -37,13 +37,13 @@ use crate::type_check::{NormalizeLocation, TypeChecker}; pub(super) fn trace<'a, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, body: &Body<'tcx>, - elements: &DenseLocationMap, + location_map: &DenseLocationMap, flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, relevant_live_locals: Vec, boring_locals: Vec, ) { - let local_use_map = &LocalUseMap::build(&relevant_live_locals, elements, body); + let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, body); // When using `-Zpolonius=next`, compute the set of loans that can reach a given region. if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() { @@ -79,7 +79,7 @@ pub(super) fn trace<'a, 'tcx>( typeck, body, flow_inits, - elements, + location_map, local_use_map, move_data, drop_data: FxIndexMap::default(), @@ -100,7 +100,7 @@ struct LivenessContext<'a, 'typeck, 'b, 'tcx> { typeck: &'a mut TypeChecker<'typeck, 'tcx>, /// Defines the `PointIndex` mapping - elements: &'a DenseLocationMap, + location_map: &'a DenseLocationMap, /// MIR we are analyzing. body: &'a Body<'tcx>, @@ -129,7 +129,7 @@ struct LivenessResults<'a, 'typeck, 'b, 'tcx> { cx: LivenessContext<'a, 'typeck, 'b, 'tcx>, /// Set of points that define the current local. - defs: BitSet, + defs: DenseBitSet, /// Points where the current variable is "use live" -- meaning /// that there is a future "full use" that may use its value. @@ -149,10 +149,10 @@ struct LivenessResults<'a, 'typeck, 'b, 'tcx> { impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { fn new(cx: LivenessContext<'a, 'typeck, 'b, 'tcx>) -> Self { - let num_points = cx.elements.num_points(); + let num_points = cx.location_map.num_points(); LivenessResults { cx, - defs: BitSet::new_empty(num_points), + defs: DenseBitSet::new_empty(num_points), use_live_at: IntervalSet::new(num_points), drop_live_at: IntervalSet::new(num_points), drop_locations: vec![], @@ -213,14 +213,14 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { fn add_extra_drop_facts(&mut self, relevant_live_locals: &[Local]) { // This collect is more necessary than immediately apparent // because these facts go into `add_drop_live_facts_for()`, - // which also writes to `all_facts`, and so this is genuinely + // which also writes to `polonius_facts`, and so this is genuinely // a simultaneous overlapping mutable borrow. // FIXME for future hackers: investigate whether this is // actually necessary; these facts come from Polonius // and probably maybe plausibly does not need to go back in. // It may be necessary to just pick out the parts of // `add_drop_live_facts_for()` that make sense. - let Some(facts) = self.cx.typeck.all_facts.as_ref() else { return }; + let Some(facts) = self.cx.typeck.polonius_facts.as_ref() else { return }; let facts_to_add: Vec<_> = { let relevant_live_locals: FxIndexSet<_> = relevant_live_locals.iter().copied().collect(); @@ -240,7 +240,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { .collect() }; - let live_at = IntervalSet::new(self.cx.elements.num_points()); + let live_at = IntervalSet::new(self.cx.location_map.num_points()); for (local, local_ty, location) in facts_to_add { self.cx.add_drop_live_facts_for(local, local_ty, &[location], &live_at); } @@ -279,7 +279,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { // * Inclusively, the block start // * Exclusively, the previous definition (if it's in this block) // * Exclusively, the previous live_at setting (an optimization) - let block_start = self.cx.elements.to_block_start(p); + let block_start = self.cx.location_map.to_block_start(p); let previous_defs = self.defs.last_set_in(block_start..=p); let previous_live_at = self.use_live_at.last_set_in(block_start..=p); @@ -303,12 +303,12 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { // terminators of predecessor basic blocks. Push those onto the // stack so that the next iteration(s) will process them. - let block = self.cx.elements.to_location(block_start).block; + let block = self.cx.location_map.to_location(block_start).block; self.stack.extend( self.cx.body.basic_blocks.predecessors()[block] .iter() .map(|&pred_bb| self.cx.body.terminator_loc(pred_bb)) - .map(|pred_loc| self.cx.elements.point_from_location(pred_loc)), + .map(|pred_loc| self.cx.location_map.point_from_location(pred_loc)), ); } } @@ -331,7 +331,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { // Find the drops where `local` is initialized. for drop_point in self.cx.local_use_map.drops(local) { - let location = self.cx.elements.to_location(drop_point); + let location = self.cx.location_map.to_location(drop_point); debug_assert_eq!(self.cx.body.terminator_loc(location.block), location,); if self.cx.initialized_at_terminator(location.block, mpi) @@ -367,7 +367,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { debug!( "compute_drop_live_points_for_block(mpi={:?}, term_point={:?})", self.cx.move_data.move_paths[mpi].place, - self.cx.elements.to_location(term_point), + self.cx.location_map.to_location(term_point), ); // We are only invoked with terminators where `mpi` is @@ -377,12 +377,15 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { // Otherwise, scan backwards through the statements in the // block. One of them may be either a definition or use // live point. - let term_location = self.cx.elements.to_location(term_point); + let term_location = self.cx.location_map.to_location(term_point); debug_assert_eq!(self.cx.body.terminator_loc(term_location.block), term_location,); let block = term_location.block; - let entry_point = self.cx.elements.entry_point(term_location.block); + let entry_point = self.cx.location_map.entry_point(term_location.block); for p in (entry_point..term_point).rev() { - debug!("compute_drop_live_points_for_block: p = {:?}", self.cx.elements.to_location(p)); + debug!( + "compute_drop_live_points_for_block: p = {:?}", + self.cx.location_map.to_location(p) + ); if self.defs.contains(p) { debug!("compute_drop_live_points_for_block: def site"); @@ -428,7 +431,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { } let pred_term_loc = self.cx.body.terminator_loc(pred_block); - let pred_term_point = self.cx.elements.point_from_location(pred_term_loc); + let pred_term_point = self.cx.location_map.point_from_location(pred_term_loc); // If the terminator of this predecessor either *assigns* // our value or is a "normal use", then stop. @@ -523,7 +526,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { /// points `live_at`. fn add_use_live_facts_for(&mut self, value: Ty<'tcx>, live_at: &IntervalSet) { debug!("add_use_live_facts_for(value={:?})", value); - Self::make_all_regions_live(self.elements, self.typeck, value, live_at); + Self::make_all_regions_live(self.location_map, self.typeck, value, live_at); } /// Some variable with type `live_ty` is "drop live" at `location` @@ -547,7 +550,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { dropped_local, dropped_ty, drop_locations, - values::pretty_print_points(self.elements, live_at.iter()), + values::pretty_print_points(self.location_map, live_at.iter()), ); let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({ @@ -574,19 +577,19 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { // All things in the `outlives` array may be touched by // the destructor and must be live at this point. for &kind in &drop_data.dropck_result.kinds { - Self::make_all_regions_live(self.elements, self.typeck, kind, live_at); + Self::make_all_regions_live(self.location_map, self.typeck, kind, live_at); polonius::legacy::emit_drop_facts( self.typeck.tcx(), dropped_local, &kind, self.typeck.universal_regions, - self.typeck.all_facts, + self.typeck.polonius_facts, ); } } fn make_all_regions_live( - elements: &DenseLocationMap, + location_map: &DenseLocationMap, typeck: &mut TypeChecker<'_, 'tcx>, value: impl TypeVisitable> + Relate>, live_at: &IntervalSet, @@ -594,7 +597,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { debug!("make_all_regions_live(value={:?})", value); debug!( "make_all_regions_live: live_at={}", - values::pretty_print_points(elements, live_at.iter()), + values::pretty_print_points(location_map, live_at.iter()), ); value.visit_with(&mut for_liveness::FreeRegionsVisitor { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 10fb8a399a26..a1979c8b8aba 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -49,7 +49,7 @@ use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet}; use crate::diagnostics::UniverseInfo; use crate::member_constraints::MemberConstraintSet; use crate::polonius::PoloniusContext; -use crate::polonius::legacy::{AllFacts, LocationTable}; +use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable}; use crate::region_infer::TypeTest; use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderIndices}; use crate::renumber::RegionCtxt; @@ -98,29 +98,29 @@ mod relate_tys; /// - `body` -- MIR body to type-check /// - `promoted` -- map of promoted constants within `body` /// - `universal_regions` -- the universal regions from `body`s function signature -/// - `location_table` -- MIR location map of `body` +/// - `location_table` -- for datalog polonius, the map between `Location`s and `RichLocation`s /// - `borrow_set` -- information about borrows occurring in `body` -/// - `all_facts` -- when using Polonius, this is the generated set of Polonius facts +/// - `polonius_facts` -- when using Polonius, this is the generated set of Polonius facts /// - `flow_inits` -- results of a maybe-init dataflow analysis /// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis -/// - `elements` -- MIR region map +/// - `location_map` -- map between MIR `Location` and `PointIndex` pub(crate) fn type_check<'a, 'tcx>( infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, promoted: &IndexSlice>, universal_regions: UniversalRegions<'tcx>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, borrow_set: &BorrowSet<'tcx>, - all_facts: &mut Option, + polonius_facts: &mut Option, flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, - elements: Rc, + location_map: Rc, ) -> MirTypeckResults<'tcx> { let implicit_region_bound = ty::Region::new_var(infcx.tcx, universal_regions.fr_fn_body); let mut constraints = MirTypeckRegionConstraints { placeholder_indices: PlaceholderIndices::default(), placeholder_index_to_region: IndexVec::default(), - liveness_constraints: LivenessValues::with_specific_points(Rc::clone(&elements)), + liveness_constraints: LivenessValues::with_specific_points(Rc::clone(&location_map)), outlives_constraints: OutlivesConstraintSet::default(), member_constraints: MemberConstraintSet::default(), type_tests: Vec::default(), @@ -165,7 +165,7 @@ pub(crate) fn type_check<'a, 'tcx>( reported_errors: Default::default(), universal_regions: &universal_region_relations.universal_regions, location_table, - all_facts, + polonius_facts, borrow_set, constraints: &mut constraints, polonius_context: &mut polonius_context, @@ -180,7 +180,7 @@ pub(crate) fn type_check<'a, 'tcx>( typeck.equate_inputs_and_outputs(body, &normalized_inputs_and_output); typeck.check_signature_annotation(body); - liveness::generate(&mut typeck, body, &elements, flow_inits, move_data); + liveness::generate(&mut typeck, body, &location_map, flow_inits, move_data); let opaque_type_values = opaque_types::take_opaques_and_register_member_constraints(&mut typeck); @@ -298,7 +298,7 @@ impl<'a, 'b, 'tcx> 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, @@ -495,14 +495,14 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // Use new sets of constraints and closure bounds so that we can // modify their locations. - let all_facts = &mut None; + let polonius_facts = &mut None; let mut constraints = Default::default(); let mut liveness_constraints = LivenessValues::without_specific_points(Rc::new(DenseLocationMap::new(promoted_body))); // Don't try to add borrow_region facts for the promoted MIR let mut swap_constraints = |this: &mut Self| { - mem::swap(this.typeck.all_facts, all_facts); + mem::swap(this.typeck.polonius_facts, polonius_facts); mem::swap(&mut this.typeck.constraints.outlives_constraints, &mut constraints); mem::swap(&mut this.typeck.constraints.liveness_constraints, &mut liveness_constraints); }; @@ -560,8 +560,8 @@ struct TypeChecker<'a, 'tcx> { implicit_region_bound: ty::Region<'tcx>, reported_errors: FxIndexSet<(Ty<'tcx>, Span)>, universal_regions: &'a UniversalRegions<'tcx>, - location_table: &'a LocationTable, - all_facts: &'a mut Option, + location_table: &'a PoloniusLocationTable, + polonius_facts: &'a mut Option, borrow_set: &'a BorrowSet<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, /// When using `-Zpolonius=next`, the helper data used to create polonius constraints. @@ -927,7 +927,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 +962,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 +1226,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 +2170,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 +2179,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ty_right, common_ty, location.to_locations(), - ConstraintCategory::Boring, + ConstraintCategory::CallArgument(None), ) { span_mirbug!( self, @@ -2327,18 +2328,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { borrowed_place: &Place<'tcx>, ) { // These constraints are only meaningful during borrowck: - let Self { borrow_set, location_table, all_facts, constraints, .. } = self; + let Self { borrow_set, location_table, polonius_facts, constraints, .. } = self; // In Polonius mode, we also push a `loan_issued_at` fact // linking the loan to the region (in some cases, though, // there is no loan associated with this borrow expression -- // that occurs when we are borrowing an unsafe place, for // example). - if let Some(all_facts) = all_facts { + if let Some(polonius_facts) = polonius_facts { let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation"); if let Some(borrow_index) = borrow_set.get_index_of(&location) { let region_vid = borrow_region.as_var(); - all_facts.loan_issued_at.push(( + polonius_facts.loan_issued_at.push(( region_vid.into(), borrow_index, location_table.mid_index(location), 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..b5aba86079fc 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -3,28 +3,22 @@ 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.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" [[package]] name = "bitflags" @@ -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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "gimli" version = "0.31.1" @@ -228,31 +231,37 @@ 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]] name = "libc" -version = "0.2.155" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets", @@ -281,50 +290,45 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "object" -version = "0.36.2" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 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", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[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", ] @@ -342,9 +346,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustc_codegen_cranelift" @@ -366,30 +370,24 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" 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.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" dependencies = [ "proc-macro2", "quote", @@ -421,21 +419,15 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.14" 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" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[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..bfdbc3e768a6 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"] } @@ -23,6 +23,14 @@ libloading = { version = "0.8.0", optional = true } smallvec = "1.8.1" [patch.crates-io] +# Uncomment to use an unreleased version of cranelift +#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-28.0.0", version = "0.115.0" } +#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-28.0.0", version = "0.115.0" } +#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-28.0.0", version = "0.115.0" } +#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-28.0.0", version = "0.115.0" } +#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-28.0.0", version = "0.115.0" } +#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-28.0.0", version = "0.115.0" } + # Uncomment to use local checkout of cranelift #cranelift-codegen = { path = "../wasmtime/cranelift/codegen" } #cranelift-frontend = { path = "../wasmtime/cranelift/frontend" } 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/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index e47e98299162..a73e3c87d43d 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -33,14 +33,7 @@ pub(crate) fn build_sysroot( let cg_clif_dylib_path = match cg_clif_dylib_src { CodegenBackend::Local(src_path) => { // Copy the backend - let cg_clif_dylib_path = if cfg!(windows) { - // Windows doesn't have rpath support, so the cg_clif dylib needs to be next to the - // binaries. - dist_dir.join("bin") - } else { - dist_dir.join("lib") - } - .join(src_path.file_name().unwrap()); + let cg_clif_dylib_path = dist_dir.join("lib").join(src_path.file_name().unwrap()); try_hard_link(src_path, &cg_clif_dylib_path); CodegenBackend::Local(cg_clif_dylib_path) } @@ -102,19 +95,14 @@ pub(crate) fn build_sysroot( .install_into_sysroot(dist_dir); } - let mut target_compiler = { - let rustc_clif = dist_dir.join(wrapper_base_name.replace("____", "rustc-clif")); - let rustdoc_clif = dist_dir.join(wrapper_base_name.replace("____", "rustdoc-clif")); - - Compiler { - cargo: bootstrap_host_compiler.cargo.clone(), - rustc: rustc_clif.clone(), - rustdoc: rustdoc_clif.clone(), - rustflags: vec![], - rustdocflags: vec![], - triple: target_triple, - runner: vec![], - } + let mut target_compiler = Compiler { + cargo: bootstrap_host_compiler.cargo.clone(), + rustc: dist_dir.join(wrapper_base_name.replace("____", "rustc-clif")), + rustdoc: dist_dir.join(wrapper_base_name.replace("____", "rustdoc-clif")), + rustflags: vec![], + rustdocflags: vec![], + triple: target_triple, + runner: vec![], }; if !is_native { target_compiler.set_cross_linker_and_runner(); 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/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 08736db8ba0c..8de419a0c4eb 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -73,8 +73,6 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ "example/arbitrary_self_types_pointers_and_wrappers.rs", &[], ), - TestCase::build_lib("build.alloc_system", "example/alloc_system.rs", "lib"), - TestCase::build_bin_and_run("aot.alloc_example", "example/alloc_example.rs", &[]), TestCase::jit_bin("jit.std_example", "example/std_example.rs", "arg"), TestCase::build_bin_and_run("aot.std_example", "example/std_example.rs", &["arg"]), TestCase::build_bin_and_run("aot.dst_field_align", "example/dst-field-align.rs", &[]), @@ -89,7 +87,6 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ &[], ), TestCase::build_bin_and_run("aot.float-minmax-pass", "example/float-minmax-pass.rs", &[]), - TestCase::build_bin_and_run("aot.mod_bench", "example/mod_bench.rs", &[]), TestCase::build_bin_and_run("aot.issue-72793", "example/issue-72793.rs", &[]), TestCase::build_bin("aot.issue-59326", "example/issue-59326.rs"), TestCase::build_bin_and_run("aot.neon", "example/neon.rs", &[]), diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index b63597f60fc6..f578cbef35e6 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 @@ -31,15 +21,12 @@ aot.mini_core_hello_world testsuite.base_sysroot aot.arbitrary_self_types_pointers_and_wrappers aot.issue_91827_extern_types -build.alloc_system -aot.alloc_example jit.std_example aot.std_example aot.dst_field_align aot.subslice-patterns-const-eval aot.track-caller-attribute aot.float-minmax-pass -aot.mod_bench aot.issue-72793 aot.issue-59326 aot.neon diff --git a/compiler/rustc_codegen_cranelift/example/alloc_example.rs b/compiler/rustc_codegen_cranelift/example/alloc_example.rs deleted file mode 100644 index da70ca794398..000000000000 --- a/compiler/rustc_codegen_cranelift/example/alloc_example.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![feature(start, core_intrinsics, alloc_error_handler, lang_items)] -#![allow(internal_features)] -#![no_std] - -extern crate alloc; -extern crate alloc_system; - -use alloc::boxed::Box; - -use alloc_system::System; - -#[global_allocator] -static ALLOC: System = System; - -#[cfg_attr(unix, link(name = "c"))] -#[cfg_attr(target_env = "msvc", link(name = "msvcrt"))] -extern "C" { - fn puts(s: *const u8) -> i32; -} - -#[panic_handler] -fn panic_handler(_: &core::panic::PanicInfo<'_>) -> ! { - core::intrinsics::abort(); -} - -#[alloc_error_handler] -fn alloc_error_handler(_: alloc::alloc::Layout) -> ! { - core::intrinsics::abort(); -} - -#[lang = "eh_personality"] -fn eh_personality() -> ! { - loop {} -} - -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { - let world: Box<&str> = Box::new("Hello World!\0"); - unsafe { - puts(*world as *const str as *const u8); - } - - 0 -} diff --git a/compiler/rustc_codegen_cranelift/example/alloc_system.rs b/compiler/rustc_codegen_cranelift/example/alloc_system.rs deleted file mode 100644 index 2884c9c32ae4..000000000000 --- a/compiler/rustc_codegen_cranelift/example/alloc_system.rs +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) - -#![no_std] - -pub struct System; - -#[cfg(any(windows, unix, target_os = "redox"))] -mod realloc_fallback { - use core::alloc::{GlobalAlloc, Layout}; - use core::{cmp, ptr}; - impl super::System { - pub(crate) unsafe fn realloc_fallback( - &self, - ptr: *mut u8, - old_layout: Layout, - new_size: usize, - ) -> *mut u8 { - // Docs for GlobalAlloc::realloc require this to be valid: - let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); - let new_ptr = GlobalAlloc::alloc(self, new_layout); - if !new_ptr.is_null() { - let size = cmp::min(old_layout.size(), new_size); - ptr::copy_nonoverlapping(ptr, new_ptr, size); - GlobalAlloc::dealloc(self, ptr, old_layout); - } - new_ptr - } - } -} -#[cfg(any(unix, target_os = "redox"))] -mod platform { - use core::alloc::{GlobalAlloc, Layout}; - use core::ffi::c_void; - use core::ptr; - - use System; - extern "C" { - fn posix_memalign(memptr: *mut *mut c_void, align: usize, size: usize) -> i32; - fn free(p: *mut c_void); - } - unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - aligned_malloc(&layout) - } - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - let ptr = self.alloc(layout.clone()); - if !ptr.is_null() { - ptr::write_bytes(ptr, 0, layout.size()); - } - ptr - } - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - free(ptr as *mut c_void) - } - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - self.realloc_fallback(ptr, layout, new_size) - } - } - unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { - let mut out = ptr::null_mut(); - let ret = posix_memalign(&mut out, layout.align(), layout.size()); - if ret != 0 { ptr::null_mut() } else { out as *mut u8 } - } -} -#[cfg(windows)] -#[allow(nonstandard_style)] -mod platform { - use core::alloc::{GlobalAlloc, Layout}; - - use System; - type LPVOID = *mut u8; - type HANDLE = LPVOID; - type SIZE_T = usize; - type DWORD = u32; - type BOOL = i32; - extern "system" { - fn GetProcessHeap() -> HANDLE; - fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; - fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; - fn GetLastError() -> DWORD; - } - #[repr(C)] - struct Header(*mut u8); - const HEAP_ZERO_MEMORY: DWORD = 0x00000008; - unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header { - &mut *(ptr as *mut Header).sub(1) - } - unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 { - let aligned = ptr.add(align - (ptr as usize & (align - 1))); - *get_header(aligned) = Header(ptr); - aligned - } - #[inline] - unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut u8 { - let size = layout.size() + layout.align(); - let ptr = HeapAlloc(GetProcessHeap(), flags, size); - (if ptr.is_null() { ptr } else { align_ptr(ptr, layout.align()) }) as *mut u8 - } - unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - allocate_with_flags(layout, 0) - } - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - allocate_with_flags(layout, HEAP_ZERO_MEMORY) - } - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - let header = get_header(ptr); - let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID); - debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError()); - } - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - self.realloc_fallback(ptr, layout, new_size) - } - } -} diff --git a/compiler/rustc_codegen_cranelift/example/mod_bench.rs b/compiler/rustc_codegen_cranelift/example/mod_bench.rs deleted file mode 100644 index 11a3e8fc72d8..000000000000 --- a/compiler/rustc_codegen_cranelift/example/mod_bench.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![feature(start, core_intrinsics, lang_items)] -#![allow(internal_features)] -#![no_std] - -#[cfg_attr(unix, link(name = "c"))] -#[cfg_attr(target_env = "msvc", link(name = "msvcrt"))] -extern "C" {} - -#[panic_handler] -fn panic_handler(_: &core::panic::PanicInfo<'_>) -> ! { - core::intrinsics::abort(); -} - -#[lang = "eh_personality"] -fn eh_personality() {} - -// Required for rustc_codegen_llvm -#[no_mangle] -unsafe extern "C" fn _Unwind_Resume() { - core::intrinsics::unreachable(); -} - -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { - for i in 2..10_000_000 { - black_box((i + 1) % i); - } - - 0 -} - -#[inline(never)] -fn black_box(i: u32) { - if i != 1 { - core::intrinsics::abort(); - } -} 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..e4c3dd708fd9 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-10" components = ["rust-src", "rustc-dev", "llvm-tools"] profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs index 1e14f41d4a2c..ebbb68796105 100644 --- a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs @@ -16,7 +16,7 @@ fn main() { if let Some(name) = option_env!("BUILTIN_BACKEND") { rustflags.push(format!("-Zcodegen-backend={name}")); } else { - let dylib = sysroot.join(if cfg!(windows) { "bin" } else { "lib" }).join( + let dylib = sysroot.join("lib").join( env::consts::DLL_PREFIX.to_string() + "rustc_codegen_cranelift" + env::consts::DLL_SUFFIX, diff --git a/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs index a27b9983bf18..528031af82a8 100644 --- a/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs @@ -11,7 +11,7 @@ fn main() { sysroot = sysroot.parent().unwrap(); } - let cg_clif_dylib_path = sysroot.join(if cfg!(windows) { "bin" } else { "lib" }).join( + let cg_clif_dylib_path = sysroot.join("lib").join( env::consts::DLL_PREFIX.to_string() + "rustc_codegen_cranelift" + env::consts::DLL_SUFFIX, ); diff --git a/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs b/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs index 1cad312bb791..6ebe060d8bbd 100644 --- a/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs @@ -11,7 +11,7 @@ fn main() { sysroot = sysroot.parent().unwrap(); } - let cg_clif_dylib_path = sysroot.join(if cfg!(windows) { "bin" } else { "lib" }).join( + let cg_clif_dylib_path = sysroot.join("lib").join( env::consts::DLL_PREFIX.to_string() + "rustc_codegen_cranelift" + env::consts::DLL_SUFFIX, ); diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index e291ec204649..e569da90cf7b 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -128,7 +128,11 @@ rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift # 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 +200,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/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index 734574338d04..025667e66b2a 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -62,9 +62,8 @@ pub(crate) fn maybe_codegen<'tcx>( } } -pub(crate) fn maybe_codegen_checked<'tcx>( +pub(crate) fn maybe_codegen_mul_checked<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, - bin_op: BinOp, lhs: CValue<'tcx>, rhs: CValue<'tcx>, ) -> Option> { @@ -78,32 +77,19 @@ pub(crate) fn maybe_codegen_checked<'tcx>( let is_signed = type_sign(lhs.layout().ty); - match bin_op { - BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => unreachable!(), - BinOp::Add | BinOp::Sub => None, - BinOp::Mul => { - let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]); - let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty)); - let param_types = vec![ - AbiParam::special(fx.pointer_type, ArgumentPurpose::StructReturn), - AbiParam::new(types::I128), - AbiParam::new(types::I128), - ]; - let args = [out_place.to_ptr().get_addr(fx), lhs.load_scalar(fx), rhs.load_scalar(fx)]; - fx.lib_call( - if is_signed { "__rust_i128_mulo" } else { "__rust_u128_mulo" }, - param_types, - vec![], - &args, - ); - Some(out_place.to_cvalue(fx)) - } - BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(), - BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow => unreachable!(), - BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"), - BinOp::Div | BinOp::Rem => unreachable!(), - BinOp::Cmp => unreachable!(), - BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => unreachable!(), - BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => unreachable!(), - } + let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]); + let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty)); + let param_types = vec![ + AbiParam::special(fx.pointer_type, ArgumentPurpose::StructReturn), + AbiParam::new(types::I128), + AbiParam::new(types::I128), + ]; + let args = [out_place.to_ptr().get_addr(fx), lhs.load_scalar(fx), rhs.load_scalar(fx)]; + fx.lib_call( + if is_signed { "__rust_i128_mulo" } else { "__rust_u128_mulo" }, + param_types, + vec![], + &args, + ); + Some(out_place.to_cvalue(fx)) } 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/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs index fb0eed07c197..ffd47cace380 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs @@ -73,12 +73,14 @@ impl Drop for TimingGuard { impl cranelift_codegen::timing::Profiler for MeasuremeProfiler { fn start_pass(&self, pass: cranelift_codegen::timing::Pass) -> Box { - let mut timing_guard = - TimingGuard { profiler: std::mem::ManuallyDrop::new(self.0.clone()), inner: None }; + let mut timing_guard = Box::new(TimingGuard { + profiler: std::mem::ManuallyDrop::new(self.0.clone()), + inner: None, + }); timing_guard.inner = Some( unsafe { &*(&*timing_guard.profiler as &SelfProfilerRef as *const SelfProfilerRef) } .generic_activity(pass.description()), ); - Box::new(timing_guard) + timing_guard } } 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_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs index fb18f45d7dca..f44e2459a784 100644 --- a/compiler/rustc_codegen_cranelift/src/num.rs +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -2,10 +2,10 @@ use crate::prelude::*; -pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option { +pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> IntCC { use BinOp::*; use IntCC::*; - Some(match bin_op { + match bin_op { Eq => Equal, Lt => { if signed { @@ -36,8 +36,8 @@ pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option { UnsignedGreaterThan } } - _ => return None, - }) + _ => unreachable!(), + } } fn codegen_three_way_compare<'tcx>( @@ -48,8 +48,8 @@ fn codegen_three_way_compare<'tcx>( ) -> CValue<'tcx> { // This emits `(lhs > rhs) - (lhs < rhs)`, which is cranelift's preferred form per // - let gt_cc = crate::num::bin_op_to_intcc(BinOp::Gt, signed).unwrap(); - let lt_cc = crate::num::bin_op_to_intcc(BinOp::Lt, signed).unwrap(); + let gt_cc = crate::num::bin_op_to_intcc(BinOp::Gt, signed); + let lt_cc = crate::num::bin_op_to_intcc(BinOp::Lt, signed); let gt = fx.bcx.ins().icmp(gt_cc, lhs, rhs); let lt = fx.bcx.ins().icmp(lt_cc, lhs, rhs); let val = fx.bcx.ins().isub(gt, lt); @@ -63,11 +63,7 @@ fn codegen_compare_bin_op<'tcx>( lhs: Value, rhs: Value, ) -> CValue<'tcx> { - if bin_op == BinOp::Cmp { - return codegen_three_way_compare(fx, signed, lhs, rhs); - } - - let intcc = crate::num::bin_op_to_intcc(bin_op, signed).unwrap(); + let intcc = crate::num::bin_op_to_intcc(bin_op, signed); let val = fx.bcx.ins().icmp(intcc, lhs, rhs); CValue::by_val(val, fx.layout_of(fx.tcx.types.bool)) } @@ -79,7 +75,7 @@ pub(crate) fn codegen_binop<'tcx>( in_rhs: CValue<'tcx>, ) -> CValue<'tcx> { match bin_op { - BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt | BinOp::Cmp => { + BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { match in_lhs.layout().ty.kind() { ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => { let signed = type_sign(in_lhs.layout().ty); @@ -91,6 +87,16 @@ pub(crate) fn codegen_binop<'tcx>( _ => {} } } + BinOp::Cmp => match in_lhs.layout().ty.kind() { + ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => { + let signed = type_sign(in_lhs.layout().ty); + let lhs = in_lhs.load_scalar(fx); + let rhs = in_rhs.load_scalar(fx); + + return codegen_three_way_compare(fx, signed, lhs, rhs); + } + _ => {} + }, _ => {} } @@ -200,10 +206,6 @@ pub(crate) fn codegen_checked_int_binop<'tcx>( let lhs = in_lhs.load_scalar(fx); let rhs = in_rhs.load_scalar(fx); - if let Some(res) = crate::codegen_i128::maybe_codegen_checked(fx, bin_op, in_lhs, in_rhs) { - return res; - } - let signed = type_sign(in_lhs.layout().ty); let (res, has_overflow) = match bin_op { @@ -236,6 +238,10 @@ pub(crate) fn codegen_checked_int_binop<'tcx>( (val, has_overflow) } BinOp::Mul => { + if let Some(res) = crate::codegen_i128::maybe_codegen_mul_checked(fx, in_lhs, in_rhs) { + return res; + } + let ty = fx.bcx.func.dfg.value_type(lhs); match ty { types::I8 | types::I16 | types::I32 if !signed => { @@ -357,14 +363,12 @@ pub(crate) fn codegen_float_binop<'tcx>( _ => bug!(), }; - let ret_val = fx.lib_call( + fx.lib_call( name, vec![AbiParam::new(ty), AbiParam::new(ty)], vec![AbiParam::new(ty)], &[lhs, rhs], - )[0]; - - return CValue::by_val(ret_val, in_lhs.layout()); + )[0] } BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { let fltcc = match bin_op { @@ -431,13 +435,9 @@ pub(crate) fn codegen_ptr_binop<'tcx>( BinOp::Lt | BinOp::Le | BinOp::Ge | BinOp::Gt => { let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr); - let ptr_cmp = - fx.bcx.ins().icmp(bin_op_to_intcc(bin_op, false).unwrap(), lhs_ptr, rhs_ptr); - let extra_cmp = fx.bcx.ins().icmp( - bin_op_to_intcc(bin_op, false).unwrap(), - lhs_extra, - rhs_extra, - ); + let ptr_cmp = fx.bcx.ins().icmp(bin_op_to_intcc(bin_op, false), lhs_ptr, rhs_ptr); + let extra_cmp = + fx.bcx.ins().icmp(bin_op_to_intcc(bin_op, false), lhs_extra, rhs_extra); fx.bcx.ins().select(ptr_eq, extra_cmp, ptr_cmp) } diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index 028a5ab5f716..69b04dd57969 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -20,7 +20,7 @@ fn inline_attr<'gcc, 'tcx>( ) -> Option> { match inline { InlineAttr::Hint => Some(FnAttribute::Inline), - InlineAttr::Always => Some(FnAttribute::AlwaysInline), + InlineAttr::Always | InlineAttr::Force { .. } => Some(FnAttribute::AlwaysInline), InlineAttr::Never => { if cx.sess().target.arch != "amdgpu" { Some(FnAttribute::NoInline) 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/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 6aeb656c1ab4..d3aeb7f3bdeb 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -4,7 +4,7 @@ use gccjit::{Location, RValue}; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoCodegenMethods}; use rustc_data_structures::sync::Lrc; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::{self, Body, SourceScope}; use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty}; @@ -69,7 +69,7 @@ fn compute_mir_scopes<'gcc, 'tcx>( ) { // Find all scopes with variables defined in them. let variables = if cx.sess().opts.debuginfo == DebugInfo::Full { - let mut vars = BitSet::new_empty(mir.source_scopes.len()); + let mut vars = DenseBitSet::new_empty(mir.source_scopes.len()); // FIXME(eddyb) take into account that arguments always have debuginfo, // irrespective of their name (assuming full debuginfo is enabled). // NOTE(eddyb) actually, on second thought, those are always in the @@ -82,7 +82,7 @@ fn compute_mir_scopes<'gcc, 'tcx>( // Nothing to emit, of course. None }; - let mut instantiated = BitSet::new_empty(mir.source_scopes.len()); + let mut instantiated = DenseBitSet::new_empty(mir.source_scopes.len()); // Instantiate all scopes. for idx in 0..mir.source_scopes.len() { let scope = SourceScope::new(idx); @@ -101,9 +101,9 @@ fn make_mir_scope<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, _instance: Instance<'tcx>, mir: &Body<'tcx>, - variables: &Option>, + variables: &Option>, debug_context: &mut FunctionDebugContext<'tcx, (), Location<'gcc>>, - instantiated: &mut BitSet, + instantiated: &mut DenseBitSet, scope: SourceScope, ) { if instantiated.contains(scope) { 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/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index f8454fd9960b..95e0481b035e 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -37,7 +37,9 @@ fn inline_attr<'ll>(cx: &CodegenCx<'ll, '_>, inline: InlineAttr) -> Option<&'ll } match inline { InlineAttr::Hint => Some(AttributeKind::InlineHint.create_attr(cx.llcx)), - InlineAttr::Always => Some(AttributeKind::AlwaysInline.create_attr(cx.llcx)), + InlineAttr::Always | InlineAttr::Force { .. } => { + Some(AttributeKind::AlwaysInline.create_attr(cx.llcx)) + } InlineAttr::Never => { if cx.sess().target.arch != "amdgpu" { Some(AttributeKind::NoInline.create_attr(cx.llcx)) @@ -472,7 +474,11 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); } - if let Some(align) = codegen_fn_attrs.alignment { + // function alignment can be set globally with the `-Zmin-function-alignment=` flag; + // the alignment from a `#[repr(align())]` is used if it specifies a higher alignment. + if let Some(align) = + Ord::max(cx.tcx.sess.opts.unstable_opts.min_function_alignment, codegen_fn_attrs.alignment) + { llvm::set_alignment(llfn, align); } if let Some(backchain) = backchain_attr(cx) { 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/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs index 07bd0f4d1c17..e545ce386ede 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs @@ -4,7 +4,7 @@ use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext}; use rustc_codegen_ssa::traits::*; use rustc_data_structures::fx::FxHashMap; use rustc_index::Idx; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{Body, SourceScope}; use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv}; use rustc_middle::ty::{self, Instance}; @@ -27,7 +27,7 @@ pub(crate) fn compute_mir_scopes<'ll, 'tcx>( ) { // Find all scopes with variables defined in them. let variables = if cx.sess().opts.debuginfo == DebugInfo::Full { - let mut vars = BitSet::new_empty(mir.source_scopes.len()); + let mut vars = DenseBitSet::new_empty(mir.source_scopes.len()); // FIXME(eddyb) take into account that arguments always have debuginfo, // irrespective of their name (assuming full debuginfo is enabled). // NOTE(eddyb) actually, on second thought, those are always in the @@ -40,7 +40,7 @@ pub(crate) fn compute_mir_scopes<'ll, 'tcx>( // Nothing to emit, of course. None }; - let mut instantiated = BitSet::new_empty(mir.source_scopes.len()); + let mut instantiated = DenseBitSet::new_empty(mir.source_scopes.len()); let mut discriminators = FxHashMap::default(); // Instantiate all scopes. for idx in 0..mir.source_scopes.len() { @@ -63,9 +63,9 @@ fn make_mir_scope<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>, mir: &Body<'tcx>, - variables: &Option>, + variables: &Option>, debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>, - instantiated: &mut BitSet, + instantiated: &mut DenseBitSet, discriminators: &mut FxHashMap, scope: SourceScope, ) { 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/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index cdb72aba36fa..37b53bb5bea4 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -18,6 +18,7 @@ use rustc_session::parse::feature_err; use rustc_session::{Session, lint}; use rustc_span::{Ident, Span, sym}; use rustc_target::spec::{SanitizerSet, abi}; +use tracing::debug; use crate::errors; use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr}; @@ -525,6 +526,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if !attr.has_name(sym::inline) { return ia; } + if attr.is_word() { InlineAttr::Hint } else if let Some(ref items) = attr.meta_item_list() { @@ -547,6 +549,20 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { ia } }); + codegen_fn_attrs.inline = attrs.iter().fold(codegen_fn_attrs.inline, |ia, attr| { + if !attr.has_name(sym::rustc_force_inline) || !tcx.features().rustc_attrs() { + return ia; + } + + if attr.is_word() { + InlineAttr::Force { attr_span: attr.span, reason: None } + } else if let Some(val) = attr.value_str() { + InlineAttr::Force { attr_span: attr.span, reason: Some(val) } + } else { + debug!("`rustc_force_inline` not checked by attribute validation"); + ia + } + }); // naked function MUST NOT be inlined! This attribute is required for the rust compiler itself, // but not for the code generation backend because at that point the naked function will just be @@ -596,7 +612,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // is probably a poor usage of `#[inline(always)]` and easily avoided by not using the attribute. if tcx.features().target_feature_11() && tcx.is_closure_like(did.to_def_id()) - && codegen_fn_attrs.inline != InlineAttr::Always + && !codegen_fn_attrs.inline.always() { let owner_id = tcx.parent(did.to_def_id()); if tcx.def_kind(owner_id).has_codegen_attrs() { @@ -606,22 +622,28 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } - // If a function uses #[target_feature] it can't be inlined into general + // If a function uses `#[target_feature]` it can't be inlined into general // purpose functions as they wouldn't have the right target features - // enabled. For that reason we also forbid #[inline(always)] as it can't be + // enabled. For that reason we also forbid `#[inline(always)]` as it can't be // respected. - if !codegen_fn_attrs.target_features.is_empty() && codegen_fn_attrs.inline == InlineAttr::Always + // + // `#[rustc_force_inline]` doesn't need to be prohibited here, only + // `#[inline(always)]`, as forced inlining is implemented entirely within + // rustc (and so the MIR inliner can do any necessary checks for compatible target + // features). + // + // This sidesteps the LLVM blockers in enabling `target_features` + + // `inline(always)` to be used together (see rust-lang/rust#116573 and + // llvm/llvm-project#70563). + if !codegen_fn_attrs.target_features.is_empty() + && matches!(codegen_fn_attrs.inline, InlineAttr::Always) { if let Some(span) = inline_span { - tcx.dcx().span_err( - span, - "cannot use `#[inline(always)]` with \ - `#[target_feature]`", - ); + tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`"); } } - if !codegen_fn_attrs.no_sanitize.is_empty() && codegen_fn_attrs.inline == InlineAttr::Always { + if !codegen_fn_attrs.no_sanitize.is_empty() && codegen_fn_attrs.inline.always() { if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) { let hir_id = tcx.local_def_id_to_hir_id(did); tcx.node_span_lint( diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 43b8230c679e..23baab3124ec 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -2,7 +2,7 @@ //! which do not. use rustc_data_structures::graph::dominators::Dominators; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, DefLocation, Location, TerminatorKind, traversal}; @@ -16,7 +16,7 @@ use crate::traits::*; pub(crate) fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx: &FunctionCx<'a, 'tcx, Bx>, traversal_order: &[mir::BasicBlock], -) -> BitSet { +) -> DenseBitSet { let mir = fx.mir; let dominators = mir.basic_blocks.dominators(); let locals = mir @@ -44,7 +44,7 @@ pub(crate) fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( analyzer.visit_basic_block_data(bb, data); } - let mut non_ssa_locals = BitSet::new_empty(analyzer.locals.len()); + let mut non_ssa_locals = DenseBitSet::new_empty(analyzer.locals.len()); for (local, kind) in analyzer.locals.iter_enumerated() { if matches!(kind, LocalKind::Memory) { non_ssa_locals.insert(local); diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 62f69af3f2f7..3a896071bc6b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -1,7 +1,7 @@ use std::iter; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::{UnwindTerminateReason, traversal}; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout}; @@ -293,7 +293,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // So drop the builder of `start_llbb` to avoid having two at the same time. drop(start_bx); - let mut unreached_blocks = BitSet::new_filled(mir.basic_blocks.len()); + let mut unreached_blocks = DenseBitSet::new_filled(mir.basic_blocks.len()); // Codegen the body of each reachable block using our reverse postorder list. for bb in traversal_order { fx.codegen_block(bb); @@ -316,7 +316,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, fx: &mut FunctionCx<'a, 'tcx, Bx>, - memory_locals: &BitSet, + memory_locals: &DenseBitSet, ) -> Vec> { let mir = fx.mir; let mut idx = 0; diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index cac3cc587cb4..8df270abc817 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -132,9 +132,13 @@ fn prefix_and_suffix<'tcx>( let attrs = tcx.codegen_fn_attrs(instance.def_id()); let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string()); - let align = attrs.alignment.map(|a| a.bytes()).unwrap_or(4); - // See https://sourceware.org/binutils/docs/as/ARM-Directives.html for info on these directives. + // function alignment can be set globally with the `-Zmin-function-alignment=` flag; + // the alignment from a `#[repr(align())]` is used if it specifies a higher alignment. + // if no alignment is specified, an alignment of 4 bytes is used. + let min_function_alignment = tcx.sess.opts.unstable_opts.min_function_alignment; + let align = Ord::max(min_function_alignment, attrs.alignment).map(|a| a.bytes()).unwrap_or(4); + // In particular, `.arm` can also be written `.code 32` and `.thumb` as `.code 16`. let (arch_prefix, arch_suffix) = if is_arm { ( diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 3b62148abb78..31793641d75e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -93,23 +93,37 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } - if let OperandValue::Immediate(v) = cg_elem.val { + let try_init_all_same = |bx: &mut Bx, v| { let start = dest.val.llval; let size = bx.const_usize(dest.layout.size.bytes()); - // Use llvm.memset.p0i8.* to initialize all zero arrays - if bx.cx().const_to_opt_u128(v, false) == Some(0) { - let fill = bx.cx().const_u8(0); - bx.memset(start, fill, size, dest.val.align, MemFlags::empty()); - return; + // Use llvm.memset.p0i8.* to initialize all same byte arrays + if let Some(int) = bx.cx().const_to_opt_u128(v, false) { + let bytes = &int.to_le_bytes()[..cg_elem.layout.size.bytes_usize()]; + let first = bytes[0]; + if bytes[1..].iter().all(|&b| b == first) { + let fill = bx.cx().const_u8(first); + bx.memset(start, fill, size, dest.val.align, MemFlags::empty()); + return true; + } } // Use llvm.memset.p0i8.* to initialize byte arrays let v = bx.from_immediate(v); if bx.cx().val_ty(v) == bx.cx().type_i8() { bx.memset(start, v, size, dest.val.align, MemFlags::empty()); - return; + return true; } + false + }; + + match cg_elem.val { + OperandValue::Immediate(v) => { + if try_init_all_same(bx, v) { + return; + } + } + _ => (), } let count = self 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/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 0c2242b810b6..4861b7a4430f 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -12,8 +12,6 @@ const_eval_already_reported = const_eval_assume_false = `assume` called with `false` -const_eval_await_non_const = - cannot convert `{$ty}` into a future in {const_eval_const_context}s const_eval_bounds_check_failed = indexing out of bounds: the len is {$len} but the index is {$index} const_eval_call_nonzero_intrinsic = @@ -23,11 +21,6 @@ const_eval_closure_call = closures need an RFC before allowed to be called in {const_eval_const_context}s const_eval_closure_fndef_not_const = function defined here, but it is not `const` -const_eval_closure_non_const = - cannot call non-const closure in {const_eval_const_context}s - -const_eval_conditionally_const_call = - cannot call conditionally-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s const_eval_consider_dereferencing = consider dereferencing here @@ -62,10 +55,6 @@ const_eval_dealloc_incorrect_layout = const_eval_dealloc_kind_mismatch = deallocating {$alloc}, which is {$alloc_kind} memory, using {$kind} deallocation operation -const_eval_deref_coercion_non_const = - cannot perform deref coercion on `{$ty}` in {const_eval_const_context}s - .note = attempting to deref into `{$target_ty}` - .target_note = deref defined here const_eval_deref_function_pointer = accessing {$allocation} which contains a function const_eval_deref_vtable_pointer = @@ -109,9 +98,6 @@ const_eval_extern_type_field = `extern type` field does not have a known offset const_eval_fn_ptr_call = function pointers need an RFC before allowed to be called in {const_eval_const_context}s -const_eval_for_loop_into_iter_non_const = - cannot use `for` loop on `{$ty}` in {const_eval_const_context}s - const_eval_frame_note = {$times -> [0] {const_eval_frame_note_inner} *[other] [... {$times} additional calls {const_eval_frame_note_inner} ...] @@ -216,9 +202,6 @@ const_eval_long_running = .label = the const evaluator is currently interpreting this expression .help = the constant being evaluated -const_eval_match_eq_non_const = cannot match on `{$ty}` in {const_eval_const_context}s - .note = `{$ty}` cannot be compared in compile-time, and therefore cannot be used in `match`es - const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id} const_eval_memory_access_test = memory access failed @@ -249,11 +232,26 @@ const_eval_mutable_ref_escaping = If you really want global mutable state, try using an interior mutable `static` or a `static mut`. const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead + +const_eval_non_const_await = + cannot convert `{$ty}` into a future in {const_eval_const_context}s + +const_eval_non_const_closure = + cannot call {$non_or_conditionally}-const closure in {const_eval_const_context}s + +const_eval_non_const_deref_coercion = + cannot perform {$non_or_conditionally}-const deref coercion on `{$ty}` in {const_eval_const_context}s + .note = attempting to deref into `{$target_ty}` + .target_note = deref defined here + const_eval_non_const_fmt_macro_call = - cannot call non-const formatting macro in {const_eval_const_context}s + cannot call {$non_or_conditionally}-const formatting macro in {const_eval_const_context}s const_eval_non_const_fn_call = - cannot call non-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s + cannot call {$non_or_conditionally}-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s + +const_eval_non_const_for_loop_into_iter = + cannot use `for` loop on `{$ty}` in {const_eval_const_context}s const_eval_non_const_impl = impl defined here, but it is not `const` @@ -261,6 +259,20 @@ const_eval_non_const_impl = const_eval_non_const_intrinsic = cannot call non-const intrinsic `{$name}` in {const_eval_const_context}s +const_eval_non_const_match_eq = cannot match on `{$ty}` in {const_eval_const_context}s + .note = `{$ty}` cannot be compared in compile-time, and therefore cannot be used in `match`es + +const_eval_non_const_operator = + cannot call {$non_or_conditionally}-const operator in {const_eval_const_context}s + +const_eval_non_const_question_branch = + `?` is not allowed on `{$ty}` in {const_eval_const_context}s +const_eval_non_const_question_from_residual = + `?` is not allowed on `{$ty}` in {const_eval_const_context}s + +const_eval_non_const_try_block_from_output = + `try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s + const_eval_not_enough_caller_args = calling a function with fewer arguments than it requires @@ -281,8 +293,6 @@ const_eval_offset_from_unsigned_overflow = *[false] offset } than second: {$a_offset} < {$b_offset} -const_eval_operator_non_const = - cannot call non-const operator in {const_eval_const_context}s const_eval_overflow_arith = arithmetic overflow in `{$intrinsic}` const_eval_overflow_shift = @@ -325,11 +335,6 @@ const_eval_ptr_as_bytes_1 = const_eval_ptr_as_bytes_2 = the absolute address of a pointer is not known at compile-time, so such operations are not supported -const_eval_question_branch_non_const = - `?` is not allowed on `{$ty}` in {const_eval_const_context}s -const_eval_question_from_residual_non_const = - `?` is not allowed on `{$ty}` in {const_eval_const_context}s - const_eval_range = in the range {$lo}..={$hi} const_eval_range_lower = greater or equal to {$lo} const_eval_range_singular = equal to {$lo} @@ -379,8 +384,6 @@ const_eval_too_generic = const_eval_too_many_caller_args = calling a function with more arguments than it expected -const_eval_try_block_from_output_non_const = - `try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {const_eval_const_context}s const_eval_unallowed_heap_allocations = diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index e895c44199b8..7a5f6c172684 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -10,7 +10,7 @@ use rustc_attr_parsing::{ConstStability, StabilityLevel}; use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; @@ -172,7 +172,7 @@ pub struct Checker<'mir, 'tcx> { /// A set that stores for each local whether it is "transient", i.e. guaranteed to be dead /// when this MIR body returns. - transient_locals: Option>, + transient_locals: Option>, error_emitted: Option, secondary_errors: Vec>, @@ -242,7 +242,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { // And then check all `Return` in the MIR, and if a local is "maybe live" at a // `Return` then it is definitely not transient. - let mut transient = BitSet::new_filled(ccx.body.local_decls.len()); + let mut transient = DenseBitSet::new_filled(ccx.body.local_decls.len()); // Make sure to only visit reachable blocks, the dataflow engine can ICE otherwise. for (bb, data) in traversal::reachable(&ccx.body) { if matches!(data.terminator().kind, TerminatorKind::Return) { @@ -708,7 +708,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { if trait_is_const { // Trait calls are always conditionally-const. - self.check_op(ops::ConditionallyConstCall { callee, args: fn_args }); + self.check_op(ops::ConditionallyConstCall { + callee, + args: fn_args, + span: *fn_span, + call_source, + }); // FIXME(const_trait_impl): do a more fine-grained check whether this // particular trait can be const-stably called. } else { @@ -726,7 +731,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // Even if we know the callee, ensure we can use conditionally-const calls. if has_const_conditions { - self.check_op(ops::ConditionallyConstCall { callee, args: fn_args }); + self.check_op(ops::ConditionallyConstCall { + callee, + args: fn_args, + span: *fn_span, + call_source, + }); } // At this point, we are calling a function, `callee`, whose `DefId` is known... diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index ebd680ac28a2..6707ebe7d1c8 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -15,6 +15,7 @@ use rustc_middle::ty::{ suggest_constraining_type_param, }; use rustc_middle::util::{CallDesugaringKind, CallKind, call_kind}; +use rustc_session::parse::add_feature_diagnostics; use rustc_span::{BytePos, Pos, Span, Symbol, sym}; use rustc_trait_selection::traits::SelectionContext; use tracing::debug; @@ -77,6 +78,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallIndirect { pub(crate) struct ConditionallyConstCall<'tcx> { pub callee: DefId, pub args: GenericArgsRef<'tcx>, + pub span: Span, + pub call_source: CallSource, } impl<'tcx> NonConstOp<'tcx> for ConditionallyConstCall<'tcx> { @@ -91,16 +94,22 @@ impl<'tcx> NonConstOp<'tcx> for ConditionallyConstCall<'tcx> { } } - fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { - ccx.tcx.sess.create_feature_err( - errors::ConditionallyConstCall { - span, - def_path_str: ccx.tcx.def_path_str_with_args(self.callee, self.args), - def_descr: ccx.tcx.def_descr(self.callee), - kind: ccx.const_kind(), - }, - sym::const_trait_impl, - ) + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> { + let mut diag = build_error_for_const_call( + ccx, + self.callee, + self.args, + self.span, + self.call_source, + "conditionally", + |_, _, _| {}, + ); + + // Override code and mention feature. + diag.code(E0658); + add_feature_diagnostics(&mut diag, ccx.tcx.sess, sym::const_trait_impl); + + diag } } @@ -118,203 +127,59 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> { - let FnCallNonConst { callee, args, span, call_source } = *self; - let ConstCx { tcx, typing_env, .. } = *ccx; + let tcx = ccx.tcx; let caller = ccx.def_id(); - let diag_trait = |err, self_ty: Ty<'_>, trait_id| { - let trait_ref = TraitRef::from_method(tcx, trait_id, args); + let mut err = build_error_for_const_call( + ccx, + self.callee, + self.args, + self.span, + self.call_source, + "non", + |err, self_ty, trait_id| { + // FIXME(const_trait_impl): Do we need any of this on the non-const codepath? - match self_ty.kind() { - Param(param_ty) => { - debug!(?param_ty); - if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() { - let constraint = with_no_trimmed_paths!(format!( - "~const {}", - trait_ref.print_trait_sugared(), - )); - suggest_constraining_type_param( - tcx, - generics, - err, - param_ty.name.as_str(), - &constraint, - Some(trait_ref.def_id), - None, - ); - } - } - ty::Adt(..) => { - let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); - let obligation = - Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref); - let mut selcx = SelectionContext::new(&infcx); - let implsrc = selcx.select(&obligation); - if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { - // FIXME(const_trait_impl) revisit this - if !tcx.is_const_trait_impl(data.impl_def_id) { - let span = tcx.def_span(data.impl_def_id); - err.subdiagnostic(errors::NonConstImplNote { span }); + let trait_ref = TraitRef::from_method(tcx, trait_id, self.args); + + match self_ty.kind() { + Param(param_ty) => { + debug!(?param_ty); + if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() { + let constraint = with_no_trimmed_paths!(format!( + "~const {}", + trait_ref.print_trait_sugared(), + )); + suggest_constraining_type_param( + tcx, + generics, + err, + param_ty.name.as_str(), + &constraint, + Some(trait_ref.def_id), + None, + ); } } - } - _ => {} - } - }; - - let call_kind = - call_kind(tcx, ccx.typing_env, callee, args, span, call_source.from_hir_call(), None); - - debug!(?call_kind); - - let mut err = match call_kind { - CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => { - macro_rules! error { - ($err:ident) => { - tcx.dcx().create_err(errors::$err { - span, - ty: self_ty, - kind: ccx.const_kind(), - }) - }; - } - - // Don't point at the trait if this is a desugaring... - // FIXME(const_trait_impl): we could perhaps do this for `Iterator`. - match kind { - CallDesugaringKind::ForLoopIntoIter | CallDesugaringKind::ForLoopNext => { - error!(NonConstForLoopIntoIter) - } - CallDesugaringKind::QuestionBranch => { - error!(NonConstQuestionBranch) - } - CallDesugaringKind::QuestionFromResidual => { - error!(NonConstQuestionFromResidual) - } - CallDesugaringKind::TryBlockFromOutput => { - error!(NonConstTryBlockFromOutput) - } - CallDesugaringKind::Await => { - error!(NonConstAwait) - } - } - } - CallKind::FnCall { fn_trait_id, self_ty } => { - let note = match self_ty.kind() { - FnDef(def_id, ..) => { - let span = tcx.def_span(*def_id); - if ccx.tcx.is_const_fn(*def_id) { - span_bug!(span, "calling const FnDef errored when it shouldn't"); - } - - Some(errors::NonConstClosureNote::FnDef { span }) - } - FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr), - Closure(..) => Some(errors::NonConstClosureNote::Closure), - _ => None, - }; - - let mut err = tcx.dcx().create_err(errors::NonConstClosure { - span, - kind: ccx.const_kind(), - note, - }); - - diag_trait(&mut err, self_ty, fn_trait_id); - err - } - CallKind::Operator { trait_id, self_ty, .. } => { - let mut err = if let CallSource::MatchCmp = call_source { - tcx.dcx().create_err(errors::NonConstMatchEq { - span, - kind: ccx.const_kind(), - ty: self_ty, - }) - } else { - let mut sugg = None; - - if ccx.tcx.is_lang_item(trait_id, LangItem::PartialEq) { - match (args[0].unpack(), args[1].unpack()) { - (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) - if self_ty == rhs_ty - && self_ty.is_ref() - && self_ty.peel_refs().is_primitive() => - { - let mut num_refs = 0; - let mut tmp_ty = self_ty; - while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() { - num_refs += 1; - tmp_ty = *inner_ty; - } - let deref = "*".repeat(num_refs); - - if let Ok(call_str) = - ccx.tcx.sess.source_map().span_to_snippet(span) - { - if let Some(eq_idx) = call_str.find("==") { - if let Some(rhs_idx) = call_str[(eq_idx + 2)..] - .find(|c: char| !c.is_whitespace()) - { - let rhs_pos = span.lo() - + BytePos::from_usize(eq_idx + 2 + rhs_idx); - let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); - sugg = Some(errors::ConsiderDereferencing { - deref, - span: span.shrink_to_lo(), - rhs_span, - }); - } - } - } + ty::Adt(..) => { + let (infcx, param_env) = + tcx.infer_ctxt().build_with_typing_env(ccx.typing_env); + let obligation = + Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref); + let mut selcx = SelectionContext::new(&infcx); + let implsrc = selcx.select(&obligation); + if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { + // FIXME(const_trait_impl) revisit this + if !tcx.is_const_trait_impl(data.impl_def_id) { + let span = tcx.def_span(data.impl_def_id); + err.subdiagnostic(errors::NonConstImplNote { span }); } - _ => {} } } - tcx.dcx().create_err(errors::NonConstOperator { - span, - kind: ccx.const_kind(), - sugg, - }) - }; - - diag_trait(&mut err, self_ty, trait_id); - err - } - CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => { - // Check first whether the source is accessible (issue #87060) - let target = if tcx.sess.source_map().is_span_accessible(deref_target) { - Some(deref_target) - } else { - None - }; - - let mut err = tcx.dcx().create_err(errors::NonConstDerefCoercion { - span, - ty: self_ty, - kind: ccx.const_kind(), - target_ty: deref_target_ty, - deref_target: target, - }); - - diag_trait(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, Some(span))); - err - } - _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) => { - ccx.dcx().create_err(errors::NonConstFmtMacroCall { span, kind: ccx.const_kind() }) - } - _ => ccx.dcx().create_err(errors::NonConstFnCall { - span, - def_descr: ccx.tcx.def_descr(callee), - def_path_str: ccx.tcx.def_path_str_with_args(callee, args), - kind: ccx.const_kind(), - }), - }; - - err.note(format!( - "calls in {}s are limited to constant functions, \ - tuple structs and tuple variants", - ccx.const_kind(), - )); + _ => {} + } + }, + ); if let ConstContext::Static(_) = ccx.const_kind() { err.note(fluent_generated::const_eval_lazy_lock); @@ -324,6 +189,190 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { } } +/// Build an error message reporting that a function call is not const (or only +/// conditionally const). In case that this call is desugared (like an operator +/// or sugar from something like a `for` loop), try to build a better error message +/// that doesn't call it a method. +fn build_error_for_const_call<'tcx>( + ccx: &ConstCx<'_, 'tcx>, + callee: DefId, + args: ty::GenericArgsRef<'tcx>, + span: Span, + call_source: CallSource, + non_or_conditionally: &'static str, + note_trait_if_possible: impl FnOnce(&mut Diag<'tcx>, Ty<'tcx>, DefId), +) -> Diag<'tcx> { + let tcx = ccx.tcx; + + let call_kind = + call_kind(tcx, ccx.typing_env, callee, args, span, call_source.from_hir_call(), None); + + debug!(?call_kind); + + let mut err = match call_kind { + CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => { + macro_rules! error { + ($err:ident) => { + tcx.dcx().create_err(errors::$err { + span, + ty: self_ty, + kind: ccx.const_kind(), + non_or_conditionally, + }) + }; + } + + // Don't point at the trait if this is a desugaring... + // FIXME(const_trait_impl): we could perhaps do this for `Iterator`. + match kind { + CallDesugaringKind::ForLoopIntoIter | CallDesugaringKind::ForLoopNext => { + error!(NonConstForLoopIntoIter) + } + CallDesugaringKind::QuestionBranch => { + error!(NonConstQuestionBranch) + } + CallDesugaringKind::QuestionFromResidual => { + error!(NonConstQuestionFromResidual) + } + CallDesugaringKind::TryBlockFromOutput => { + error!(NonConstTryBlockFromOutput) + } + CallDesugaringKind::Await => { + error!(NonConstAwait) + } + } + } + CallKind::FnCall { fn_trait_id, self_ty } => { + let note = match self_ty.kind() { + FnDef(def_id, ..) => { + let span = tcx.def_span(*def_id); + if ccx.tcx.is_const_fn(*def_id) { + span_bug!(span, "calling const FnDef errored when it shouldn't"); + } + + Some(errors::NonConstClosureNote::FnDef { span }) + } + FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr), + Closure(..) => Some(errors::NonConstClosureNote::Closure), + _ => None, + }; + + let mut err = tcx.dcx().create_err(errors::NonConstClosure { + span, + kind: ccx.const_kind(), + note, + non_or_conditionally, + }); + + note_trait_if_possible(&mut err, self_ty, fn_trait_id); + err + } + CallKind::Operator { trait_id, self_ty, .. } => { + let mut err = if let CallSource::MatchCmp = call_source { + tcx.dcx().create_err(errors::NonConstMatchEq { + span, + kind: ccx.const_kind(), + ty: self_ty, + non_or_conditionally, + }) + } else { + let mut sugg = None; + + if ccx.tcx.is_lang_item(trait_id, LangItem::PartialEq) { + match (args[0].unpack(), args[1].unpack()) { + (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) + if self_ty == rhs_ty + && self_ty.is_ref() + && self_ty.peel_refs().is_primitive() => + { + let mut num_refs = 0; + let mut tmp_ty = self_ty; + while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() { + num_refs += 1; + tmp_ty = *inner_ty; + } + let deref = "*".repeat(num_refs); + + if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) { + if let Some(eq_idx) = call_str.find("==") { + if let Some(rhs_idx) = + call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace()) + { + let rhs_pos = + span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx); + let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); + sugg = Some(errors::ConsiderDereferencing { + deref, + span: span.shrink_to_lo(), + rhs_span, + }); + } + } + } + } + _ => {} + } + } + tcx.dcx().create_err(errors::NonConstOperator { + span, + kind: ccx.const_kind(), + sugg, + non_or_conditionally, + }) + }; + + note_trait_if_possible(&mut err, self_ty, trait_id); + err + } + CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => { + // Check first whether the source is accessible (issue #87060) + let target = if tcx.sess.source_map().is_span_accessible(deref_target) { + Some(deref_target) + } else { + None + }; + + let mut err = tcx.dcx().create_err(errors::NonConstDerefCoercion { + span, + ty: self_ty, + kind: ccx.const_kind(), + target_ty: deref_target_ty, + deref_target: target, + non_or_conditionally, + }); + + note_trait_if_possible( + &mut err, + self_ty, + tcx.require_lang_item(LangItem::Deref, Some(span)), + ); + err + } + _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) => { + ccx.dcx().create_err(errors::NonConstFmtMacroCall { + span, + kind: ccx.const_kind(), + non_or_conditionally, + }) + } + _ => ccx.dcx().create_err(errors::NonConstFnCall { + span, + def_descr: ccx.tcx.def_descr(callee), + def_path_str: ccx.tcx.def_path_str_with_args(callee, args), + kind: ccx.const_kind(), + non_or_conditionally, + }), + }; + + err.note(format!( + "calls in {}s are limited to constant functions, \ + tuple structs and tuple variants", + ccx.const_kind(), + )); + + err +} + /// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function. /// /// Contains the name of the feature that would allow the use of this function. diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 57534540019b..3fe78171cd94 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -174,16 +174,7 @@ pub(crate) struct NonConstFmtMacroCall { #[primary_span] pub span: Span, pub kind: ConstContext, -} - -#[derive(Diagnostic)] -#[diag(const_eval_conditionally_const_call)] -pub(crate) struct ConditionallyConstCall { - #[primary_span] - pub span: Span, - pub def_path_str: String, - pub def_descr: &'static str, - pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] @@ -194,6 +185,7 @@ pub(crate) struct NonConstFnCall { pub def_path_str: String, pub def_descr: &'static str, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] @@ -293,68 +285,75 @@ pub struct RawBytesNote { // FIXME(fee1-dead) do not use stringly typed `ConstContext` #[derive(Diagnostic)] -#[diag(const_eval_match_eq_non_const, code = E0015)] +#[diag(const_eval_non_const_match_eq, code = E0015)] #[note] pub struct NonConstMatchEq<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_for_loop_into_iter_non_const, code = E0015)] +#[diag(const_eval_non_const_for_loop_into_iter, code = E0015)] pub struct NonConstForLoopIntoIter<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_question_branch_non_const, code = E0015)] +#[diag(const_eval_non_const_question_branch, code = E0015)] pub struct NonConstQuestionBranch<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_question_from_residual_non_const, code = E0015)] +#[diag(const_eval_non_const_question_from_residual, code = E0015)] pub struct NonConstQuestionFromResidual<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_try_block_from_output_non_const, code = E0015)] +#[diag(const_eval_non_const_try_block_from_output, code = E0015)] pub struct NonConstTryBlockFromOutput<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_await_non_const, code = E0015)] +#[diag(const_eval_non_const_await, code = E0015)] pub struct NonConstAwait<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_closure_non_const, code = E0015)] +#[diag(const_eval_non_const_closure, code = E0015)] pub struct NonConstClosure { #[primary_span] pub span: Span, pub kind: ConstContext, #[subdiagnostic] pub note: Option, + pub non_or_conditionally: &'static str, } #[derive(Subdiagnostic)] @@ -381,17 +380,18 @@ pub struct ConsiderDereferencing { } #[derive(Diagnostic)] -#[diag(const_eval_operator_non_const, code = E0015)] +#[diag(const_eval_non_const_operator, code = E0015)] pub struct NonConstOperator { #[primary_span] pub span: Span, pub kind: ConstContext, #[subdiagnostic] pub sugg: Option, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_deref_coercion_non_const, code = E0015)] +#[diag(const_eval_non_const_deref_coercion, code = E0015)] #[note] pub struct NonConstDerefCoercion<'tcx> { #[primary_span] @@ -401,6 +401,7 @@ pub struct NonConstDerefCoercion<'tcx> { pub target_ty: Ty<'tcx>, #[note(const_eval_target_note)] pub deref_target: Option, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] 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_data_structures/src/graph/implementation/mod.rs b/compiler/rustc_data_structures/src/graph/implementation/mod.rs index 43fdfe6ee0d7..7724e9347d82 100644 --- a/compiler/rustc_data_structures/src/graph/implementation/mod.rs +++ b/compiler/rustc_data_structures/src/graph/implementation/mod.rs @@ -22,7 +22,7 @@ use std::fmt::Debug; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use tracing::debug; #[cfg(test)] @@ -214,7 +214,7 @@ impl Graph { direction: Direction, entry_node: NodeIndex, ) -> Vec { - let mut visited = BitSet::new_empty(self.len_nodes()); + let mut visited = DenseBitSet::new_empty(self.len_nodes()); let mut stack = vec![]; let mut result = Vec::with_capacity(self.len_nodes()); let mut push_node = |stack: &mut Vec<_>, node: NodeIndex| { @@ -287,7 +287,7 @@ impl<'g, N: Debug, E: Debug> Iterator for AdjacentEdges<'g, N, E> { pub struct DepthFirstTraversal<'g, N, E> { graph: &'g Graph, stack: Vec, - visited: BitSet, + visited: DenseBitSet, direction: Direction, } @@ -297,7 +297,7 @@ impl<'g, N: Debug, E: Debug> DepthFirstTraversal<'g, N, E> { start_node: NodeIndex, direction: Direction, ) -> Self { - let mut visited = BitSet::new_empty(graph.len_nodes()); + let mut visited = DenseBitSet::new_empty(graph.len_nodes()); visited.insert(start_node.node_id()); DepthFirstTraversal { graph, stack: vec![start_node], visited, direction } } diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs index cbc6664d853a..ecda7d3fba83 100644 --- a/compiler/rustc_data_structures/src/graph/iterate/mod.rs +++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs @@ -1,6 +1,6 @@ use std::ops::ControlFlow; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexSlice, IndexVec}; use super::{DirectedGraph, StartNode, Successors}; @@ -78,7 +78,7 @@ where { graph: G, stack: Vec, - visited: BitSet, + visited: DenseBitSet, } impl DepthFirstSearch @@ -86,7 +86,7 @@ where G: DirectedGraph + Successors, { pub fn new(graph: G) -> Self { - Self { stack: vec![], visited: BitSet::new_empty(graph.num_nodes()), graph } + Self { stack: vec![], visited: DenseBitSet::new_empty(graph.num_nodes()), graph } } /// Version of `push_start_node` that is convenient for chained @@ -207,8 +207,8 @@ where { graph: &'graph G, stack: Vec>, - visited: BitSet, - settled: BitSet, + visited: DenseBitSet, + settled: DenseBitSet, } impl<'graph, G> TriColorDepthFirstSearch<'graph, G> @@ -219,8 +219,8 @@ where TriColorDepthFirstSearch { graph, stack: vec![], - visited: BitSet::new_empty(graph.num_nodes()), - settled: BitSet::new_empty(graph.num_nodes()), + visited: DenseBitSet::new_empty(graph.num_nodes()), + settled: DenseBitSet::new_empty(graph.num_nodes()), } } diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 0872bd2c9acc..9cd0cc499ca3 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use std::mem; use std::num::NonZero; -use rustc_index::bit_set::{self, BitSet}; +use rustc_index::bit_set::{self, DenseBitSet}; use rustc_index::{Idx, IndexSlice, IndexVec}; use smallvec::SmallVec; @@ -544,7 +544,7 @@ where } } -impl HashStable for BitSet { +impl HashStable for DenseBitSet { fn hash_stable(&self, _ctx: &mut CTX, hasher: &mut StableHasher) { ::std::hash::Hash::hash(self, hasher); } diff --git a/compiler/rustc_data_structures/src/stable_hasher/tests.rs b/compiler/rustc_data_structures/src/stable_hasher/tests.rs index aab50a13af0e..635f241847c4 100644 --- a/compiler/rustc_data_structures/src/stable_hasher/tests.rs +++ b/compiler/rustc_data_structures/src/stable_hasher/tests.rs @@ -17,9 +17,9 @@ fn hash>(t: &T) -> Hash128 { // Check that bit set hash includes the domain size. #[test] fn test_hash_bit_set() { - use rustc_index::bit_set::BitSet; - let a: BitSet = BitSet::new_empty(1); - let b: BitSet = BitSet::new_empty(2); + use rustc_index::bit_set::DenseBitSet; + let a: DenseBitSet = DenseBitSet::new_empty(1); + let b: DenseBitSet = DenseBitSet::new_empty(2); assert_ne!(a, b); assert_ne!(hash(&a), hash(&b)); } diff --git a/compiler/rustc_data_structures/src/work_queue.rs b/compiler/rustc_data_structures/src/work_queue.rs index ca052e2eac6d..815756edfeba 100644 --- a/compiler/rustc_data_structures/src/work_queue.rs +++ b/compiler/rustc_data_structures/src/work_queue.rs @@ -1,7 +1,7 @@ use std::collections::VecDeque; use rustc_index::Idx; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; /// A work queue is a handy data structure for tracking work left to /// do. (For example, basic blocks left to process.) It is basically a @@ -11,14 +11,14 @@ use rustc_index::bit_set::BitSet; /// and also use a bit set to track occupancy. pub struct WorkQueue { deque: VecDeque, - set: BitSet, + set: DenseBitSet, } impl WorkQueue { /// Creates a new work queue that starts empty, where elements range from (0..len). #[inline] pub fn with_none(len: usize) -> Self { - WorkQueue { deque: VecDeque::with_capacity(len), set: BitSet::new_empty(len) } + WorkQueue { deque: VecDeque::with_capacity(len), set: DenseBitSet::new_empty(len) } } /// Attempt to enqueue `element` in the work queue. Returns false if it was already present. 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_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index afce877547f3..797dcd7b4d1d 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -880,7 +880,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { ) } } - /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic. + /// Show a suggestion that has multiple parts to it, always as its own subdiagnostic. /// In other words, multiple changes need to be applied as part of this suggestion. #[rustc_lint_diagnostics] pub fn multipart_suggestion_verbose( diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 58fe3ec4b858..cc5eb9c335e8 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -566,9 +566,7 @@ pub enum StashKey { /// FRU syntax MaybeFruTypo, CallAssocMethod, - TraitMissingMethod, AssociatedTypeSuggestion, - OpaqueHiddenTypeMismatch, MaybeForgetReturn, /// Query cycle detected, stashing in favor of a better error. Cycle, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index e6adbc0f0ac8..43feab94c01d 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -522,7 +522,7 @@ impl MacResult for MacEager { return Some(P(ast::Pat { id: ast::DUMMY_NODE_ID, span: e.span, - kind: PatKind::Lit(e), + kind: PatKind::Expr(e), tokens: None, })); } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 22bfda34cc0e..8bf09cf96b3f 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -486,7 +486,7 @@ impl<'a> ExtCtxt<'a> { self.pat(span, PatKind::Wild) } pub fn pat_lit(&self, span: Span, expr: P) -> P { - self.pat(span, PatKind::Lit(expr)) + self.pat(span, PatKind::Expr(expr)) } pub fn pat_ident(&self, span: Span, ident: Ident) -> P { self.pat_ident_binding_mode(span, ident, ast::BindingMode::NONE) diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 91624c7554cb..3e3f35332e0d 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -18,7 +18,7 @@ use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::validate_attr; use rustc_session::Session; use rustc_session::parse::feature_err; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym}; use thin_vec::ThinVec; use tracing::instrument; @@ -107,14 +107,11 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - // If the enabled feature is unstable, record it. if UNSTABLE_LANG_FEATURES.iter().find(|f| name == f.name).is_some() { - // When the ICE comes from core, alloc or std (approximation of the standard - // library), there's a chance that the person hitting the ICE may be using - // -Zbuild-std or similar with an untested target. The bug is probably in the - // standard library and not the compiler in that case, but that doesn't really - // matter - we want a bug report. - if features.internal(name) - && ![sym::core, sym::alloc, sym::std].contains(&crate_name) - { + // When the ICE comes a standard library crate, there's a chance that the person + // hitting the ICE may be using -Zbuild-std or similar with an untested target. + // The bug is probably in the standard library and not the compiler in that case, + // but that doesn't really matter - we want a bug report. + if features.internal(name) && !STDLIB_STABLE_CRATES.contains(&crate_name) { sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed); } @@ -133,7 +130,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - // Similar to above, detect internal lib features to suppress // the ICE message that asks for a report. - if features.internal(name) && ![sym::core, sym::alloc, sym::std].contains(&crate_name) { + if features.internal(name) && !STDLIB_STABLE_CRATES.contains(&crate_name) { sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed); } } diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 776de1988ccd..217a7aeb2d7f 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -73,7 +73,7 @@ declare_features! ( /// Allows free and inherent `async fn`s, `async` blocks, and `.await` expressions. (accepted, async_await, "1.39.0", Some(50547)), /// Allows `async || body` closures. - (accepted, async_closure, "CURRENT_RUSTC_VERSION", Some(62290)), + (accepted, async_closure, "1.85.0", Some(62290)), /// Allows async functions to be declared, implemented, and used in traits. (accepted, async_fn_in_trait, "1.75.0", Some(91611)), /// Allows all literals in attribute lists and values of key-value pairs. @@ -176,7 +176,7 @@ declare_features! ( /// Allows using the `#[diagnostic]` attribute tool namespace (accepted, diagnostic_namespace, "1.78.0", Some(111996)), /// Controls errors in trait implementations. - (accepted, do_not_recommend, "CURRENT_RUSTC_VERSION", Some(51992)), + (accepted, do_not_recommend, "1.85.0", Some(51992)), /// Allows `#[doc(alias = "...")]`. (accepted, doc_alias, "1.48.0", Some(50146)), /// Allows `..` in tuple (struct) patterns. @@ -199,7 +199,7 @@ declare_features! ( (accepted, extended_key_value_attributes, "1.54.0", Some(78835)), /// Allows using `efiapi`, `aapcs`, `sysv64` and `win64` as calling /// convention for functions with varargs. - (accepted, extended_varargs_abi_support, "CURRENT_RUSTC_VERSION", Some(100189)), + (accepted, extended_varargs_abi_support, "1.85.0", Some(100189)), /// Allows resolving absolute paths as paths from other crates. (accepted, extern_absolute_paths, "1.30.0", Some(44660)), /// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 00bb4d655790..c28a4360f6f9 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. @@ -571,7 +572,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // `#[coroutine]` attribute to be applied to closures to make them coroutines instead gated!( coroutine, Normal, template!(Word), ErrorFollowing, - EncodeCrossCrate::No, coroutines, experimental!(coroutines) + EncodeCrossCrate::No, coroutines, experimental!(coroutine) ), // RFC 3543 @@ -1018,6 +1019,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_no_mir_inline, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, "#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen" ), + rustc_attr!( + rustc_force_inline, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, EncodeCrossCrate::Yes, + "#![rustc_force_inline] forces a free function to be inlined" + ), // ========================================================================== // Internal attributes, Testing: diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 388ed9d08fa6..9aa59375706b 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -120,7 +120,7 @@ declare_features! ( /// Allows defining generators. (removed, generators, "1.21.0", Some(43122), Some("renamed to `coroutines`")), /// An extension to the `generic_associated_types` feature, allowing incomplete features. - (removed, generic_associated_types_extended, "CURRENT_RUSTC_VERSION", Some(95451), + (removed, generic_associated_types_extended, "1.85.0", Some(95451), Some( "feature needs overhaul and reimplementation pending \ better implied higher-ranked implied bounds support" diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8b5cecc3e4fe..6b88651715e0 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 @@ -331,7 +333,7 @@ declare_features! ( (unstable, hexagon_target_feature, "1.27.0", Some(44839)), (unstable, lahfsahf_target_feature, "1.78.0", Some(44839)), (unstable, loongarch_target_feature, "1.73.0", Some(44839)), - (unstable, m68k_target_feature, "CURRENT_RUSTC_VERSION", Some(134328)), + (unstable, m68k_target_feature, "1.85.0", Some(134328)), (unstable, mips_target_feature, "1.27.0", Some(44839)), (unstable, powerpc_target_feature, "1.27.0", Some(44839)), (unstable, prfchw_target_feature, "1.78.0", Some(44839)), @@ -342,7 +344,7 @@ declare_features! ( (unstable, sse4a_target_feature, "1.27.0", Some(44839)), (unstable, tbm_target_feature, "1.27.0", Some(44839)), (unstable, wasm_target_feature, "1.30.0", Some(44839)), - (unstable, x87_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), + (unstable, x87_target_feature, "1.85.0", Some(44839)), // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! @@ -378,11 +380,11 @@ declare_features! ( /// Enables experimental inline assembly support for additional architectures. (unstable, asm_experimental_arch, "1.58.0", Some(93335)), /// Enables experimental register support in inline assembly. - (unstable, asm_experimental_reg, "CURRENT_RUSTC_VERSION", Some(133416)), + (unstable, asm_experimental_reg, "1.85.0", Some(133416)), /// Allows using `label` operands in inline assembly. (unstable, asm_goto, "1.78.0", Some(119364)), /// Allows using `label` operands in inline assembly together with output operands. - (unstable, asm_goto_with_outputs, "CURRENT_RUSTC_VERSION", Some(119364)), + (unstable, asm_goto_with_outputs, "1.85.0", Some(119364)), /// Allows the `may_unwind` option in inline assembly. (unstable, asm_unwind, "1.58.0", Some(93334)), /// Allows users to enforce equality of associated constants `TraitImpl`. @@ -390,13 +392,13 @@ declare_features! ( /// Allows associated type defaults. (unstable, associated_type_defaults, "1.2.0", Some(29661)), /// Allows async functions to be called from `dyn Trait`. - (incomplete, async_fn_in_dyn_trait, "CURRENT_RUSTC_VERSION", Some(133119)), + (incomplete, async_fn_in_dyn_trait, "1.85.0", Some(133119)), /// Allows `#[track_caller]` on async functions. (unstable, async_fn_track_caller, "1.73.0", Some(110011)), /// Allows `for await` loops. (unstable, async_for_loop, "1.77.0", Some(118898)), /// Allows `async` trait bound modifier. - (unstable, async_trait_bounds, "CURRENT_RUSTC_VERSION", Some(62290)), + (unstable, async_trait_bounds, "1.85.0", Some(62290)), /// Allows using C-variadics. (unstable, c_variadic, "1.34.0", Some(44930)), /// Allows the use of `#[cfg()]`. @@ -434,7 +436,7 @@ declare_features! ( /// Allows `const || {}` closures in const contexts. (incomplete, const_closures, "1.68.0", Some(106003)), /// Allows using `~const Destruct` bounds and calling drop impls in const contexts. - (unstable, const_destruct, "CURRENT_RUSTC_VERSION", Some(133214)), + (unstable, const_destruct, "1.85.0", Some(133214)), /// Allows `for _ in _` loops in const contexts. (unstable, const_for, "1.56.0", Some(87575)), /// Be more precise when looking for live drops in a const context. @@ -458,7 +460,7 @@ declare_features! ( (unstable, decl_macro, "1.17.0", Some(39412)), /// Allows the use of default values on struct definitions and the construction of struct /// literals with the functional update syntax without a base. - (unstable, default_field_values, "CURRENT_RUSTC_VERSION", Some(132162)), + (unstable, default_field_values, "1.85.0", Some(132162)), /// Allows using `#[deprecated_safe]` to deprecate the safeness of a function or trait (unstable, deprecated_safe, "1.61.0", Some(94978)), /// Allows having using `suggestion` in the `#[deprecated]` attribute. @@ -508,7 +510,7 @@ declare_features! ( /// Allows registering static items globally, possibly across crates, to iterate over at runtime. (unstable, global_registration, "1.80.0", Some(125119)), /// Allows using guards in patterns. - (incomplete, guard_patterns, "CURRENT_RUSTC_VERSION", Some(129967)), + (incomplete, guard_patterns, "1.85.0", Some(129967)), /// Allows using `..=X` as a patterns in slices. (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)), /// Allows `if let` guard in match arms. @@ -639,9 +641,9 @@ declare_features! ( /// not changed from prior instances of the same struct (RFC #2528) (unstable, type_changing_struct_update, "1.58.0", Some(86555)), /// Allows using `unsafe<'a> &'a T` unsafe binder types. - (incomplete, unsafe_binders, "CURRENT_RUSTC_VERSION", Some(130516)), + (incomplete, unsafe_binders, "1.85.0", Some(130516)), /// Allows declaring fields `unsafe`. - (incomplete, unsafe_fields, "CURRENT_RUSTC_VERSION", Some(132922)), + (incomplete, unsafe_fields, "1.85.0", Some(132922)), /// Allows const generic parameters to be defined with types that /// are not `Sized`, e.g. `fn foo() {`. (incomplete, unsized_const_params, "1.82.0", Some(95174)), diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 6b4b716d9a80..dd96b30fefc3 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -7,7 +7,7 @@ use rustc_ast::token::CommentKind; use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::{ self as ast, AttrId, AttrStyle, DelimArgs, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, - IntTy, Label, LitKind, MetaItemInner, MetaItemLit, TraitObjectSyntax, UintTy, + IntTy, Label, LitIntType, LitKind, MetaItemInner, MetaItemLit, TraitObjectSyntax, UintTy, }; pub use rustc_ast::{ BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy, @@ -1386,8 +1386,8 @@ 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), + Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true, + 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) => { @@ -1413,8 +1413,8 @@ 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), + Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {} + 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) => { @@ -1519,6 +1519,26 @@ impl fmt::Debug for DotDotPos { } } +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub struct PatExpr<'hir> { + pub hir_id: HirId, + pub span: Span, + pub kind: PatExprKind<'hir>, +} + +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub enum PatExprKind<'hir> { + Lit { + lit: &'hir Lit, + // FIXME: move this into `Lit` and handle negated literal expressions + // once instead of matching on unop neg expressions everywhere. + negated: bool, + }, + ConstBlock(ConstBlock), + /// A path pattern for a unit struct/variant or a (maybe-associated) constant. + Path(QPath<'hir>), +} + #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum PatKind<'hir> { /// Represents a wildcard pattern (i.e., `_`). @@ -1563,11 +1583,14 @@ pub enum PatKind<'hir> { /// A reference pattern (e.g., `&mut (a, b)`). Ref(&'hir Pat<'hir>, Mutability), - /// A literal. - Lit(&'hir Expr<'hir>), + /// A literal, const block or path. + Expr(&'hir PatExpr<'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), + Range(Option<&'hir PatExpr<'hir>>, Option<&'hir PatExpr<'hir>>, RangeEnd), /// A slice pattern, `[before_0, ..., before_n, (slice, after_0, ..., after_n)?]`. /// @@ -2071,6 +2094,18 @@ impl Expr<'_> { } } + /// Check if expression is an integer literal that can be used + /// where `usize` is expected. + pub fn is_size_lit(&self) -> bool { + matches!( + self.kind, + ExprKind::Lit(Lit { + node: LitKind::Int(_, LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::Usize)), + .. + }) + ) + } + /// If `Self.kind` is `ExprKind::DropTemps(expr)`, drill down until we get a non-`DropTemps` /// `Expr`. This is used in suggestions to ignore this `ExprKind` as it is semantically /// silent, only signaling the ownership system. By doing this, suggestions that check the @@ -4141,6 +4176,10 @@ pub enum Node<'hir> { OpaqueTy(&'hir OpaqueTy<'hir>), Pat(&'hir Pat<'hir>), PatField(&'hir PatField<'hir>), + /// Needed as its own node with its own HirId for tracking + /// the unadjusted type of literals within patterns + /// (e.g. byte str literals not being of slice type). + PatExpr(&'hir PatExpr<'hir>), Arm(&'hir Arm<'hir>), Block(&'hir Block<'hir>), LetStmt(&'hir LetStmt<'hir>), @@ -4197,6 +4236,7 @@ impl<'hir> Node<'hir> { | Node::Block(..) | Node::Ctor(..) | Node::Pat(..) + | Node::PatExpr(..) | Node::Arm(..) | Node::LetStmt(..) | Node::Crate(..) diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 85e555d903b9..ef863aca0905 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -342,6 +342,9 @@ pub trait Visitor<'v>: Sized { fn visit_pat_field(&mut self, f: &'v PatField<'v>) -> Self::Result { walk_pat_field(self, f) } + fn visit_pat_expr(&mut self, expr: &'v PatExpr<'v>) -> Self::Result { + walk_pat_expr(self, expr) + } fn visit_anon_const(&mut self, c: &'v AnonConst) -> Self::Result { walk_anon_const(self, c) } @@ -685,10 +688,10 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: try_visit!(visitor.visit_ident(ident)); visit_opt!(visitor, visit_pat, optional_subpattern); } - PatKind::Lit(ref expression) => try_visit!(visitor.visit_expr(expression)), + PatKind::Expr(ref expression) => try_visit!(visitor.visit_pat_expr(expression)), PatKind::Range(ref lower_bound, ref upper_bound, _) => { - visit_opt!(visitor, visit_expr, lower_bound); - visit_opt!(visitor, visit_expr, upper_bound); + visit_opt!(visitor, visit_pat_expr, lower_bound); + visit_opt!(visitor, visit_pat_expr, upper_bound); } PatKind::Never | PatKind::Wild | PatKind::Err(_) => (), PatKind::Slice(prepatterns, ref slice_pattern, postpatterns) => { @@ -696,6 +699,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() } @@ -706,6 +713,15 @@ pub fn walk_pat_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v PatField<' visitor.visit_pat(field.pat) } +pub fn walk_pat_expr<'v, V: Visitor<'v>>(visitor: &mut V, expr: &'v PatExpr<'v>) -> V::Result { + try_visit!(visitor.visit_id(expr.hir_id)); + match &expr.kind { + PatExprKind::Lit { .. } => V::Result::output(), + PatExprKind::ConstBlock(c) => visitor.visit_inline_const(c), + PatExprKind::Path(qpath) => visitor.visit_qpath(qpath, expr.hir_id, expr.span), + } +} + pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonConst) -> V::Result { try_visit!(visitor.visit_id(constant.hir_id)); visitor.visit_nested_body(constant.body) diff --git a/compiler/rustc_hir/src/tests.rs b/compiler/rustc_hir/src/tests.rs index 953e48a6d334..e0e63d183c63 100644 --- a/compiler/rustc_hir/src/tests.rs +++ b/compiler/rustc_hir/src/tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] +#![allow(rustc::symbol_intern_string_literal)] use rustc_data_structures::stable_hasher::Hash64; use rustc_span::def_id::{DefPathHash, StableCrateId}; diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 5548a6a6ef79..974a8648bc0e 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -575,7 +575,7 @@ fn sanity_check_found_hidden_type<'tcx>( } else { let span = tcx.def_span(key.def_id); let other = ty::OpaqueHiddenType { ty: hidden_ty, span }; - Err(ty.build_mismatch_error(&other, key.def_id, tcx)?.emit()) + Err(ty.build_mismatch_error(&other, tcx)?.emit()) } } @@ -1666,7 +1666,7 @@ fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalD .collect::>() }); - let mut params_used = BitSet::new_empty(generics.own_params.len()); + let mut params_used = DenseBitSet::new_empty(generics.own_params.len()); for leaf in ty.walk() { if let GenericArgKind::Type(leaf_ty) = leaf.unpack() && let ty::Param(param) = leaf_ty.kind() @@ -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/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 0b0c92a726dd..92b18c80fd82 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -79,7 +79,7 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, TyCtxtInferExt as _}; use rustc_infer::traits::ObligationCause; diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index ca6729a5bbdf..83c69dc2ef41 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -31,7 +31,7 @@ struct Context { parent: Option<(Scope, ScopeDepth)>, } -struct RegionResolutionVisitor<'tcx> { +struct ScopeResolutionVisitor<'tcx> { tcx: TyCtxt<'tcx>, // The number of expressions and patterns visited in the current body. @@ -71,7 +71,7 @@ struct RegionResolutionVisitor<'tcx> { } /// Records the lifetime of a local variable as `cx.var_parent` -fn record_var_lifetime(visitor: &mut RegionResolutionVisitor<'_>, var_id: hir::ItemLocalId) { +fn record_var_lifetime(visitor: &mut ScopeResolutionVisitor<'_>, var_id: hir::ItemLocalId) { match visitor.cx.var_parent { None => { // this can happen in extern fn declarations like @@ -82,7 +82,7 @@ fn record_var_lifetime(visitor: &mut RegionResolutionVisitor<'_>, var_id: hir::I } } -fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx hir::Block<'tcx>) { +fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hir::Block<'tcx>) { debug!("resolve_block(blk.hir_id={:?})", blk.hir_id); let prev_cx = visitor.cx; @@ -193,7 +193,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h visitor.cx = prev_cx; } -fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) { +fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) { fn has_let_expr(expr: &Expr<'_>) -> bool { match &expr.kind { hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs), @@ -220,7 +220,7 @@ fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir visitor.cx = prev_cx; } -fn resolve_pat<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) { +fn resolve_pat<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) { visitor.record_child_scope(Scope { local_id: pat.hir_id.local_id, data: ScopeData::Node }); // If this is a binding then record the lifetime of that binding. @@ -237,7 +237,7 @@ fn resolve_pat<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, pat: &'tcx hir debug!("resolve_pat - post-increment {} pat = {:?}", visitor.expr_and_pat_count, pat); } -fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx hir::Stmt<'tcx>) { +fn resolve_stmt<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, stmt: &'tcx hir::Stmt<'tcx>) { let stmt_id = stmt.hir_id.local_id; debug!("resolve_stmt(stmt.id={:?})", stmt_id); @@ -256,7 +256,7 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h visitor.cx.parent = prev_parent; } -fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) { +fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) { debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr); let prev_cx = visitor.cx; @@ -420,10 +420,10 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h // properly, we can't miss any types. match expr.kind { - // Manually recurse over closures and inline consts, because they are the only - // case of nested bodies that share the parent environment. - hir::ExprKind::Closure(&hir::Closure { body, .. }) - | hir::ExprKind::ConstBlock(hir::ConstBlock { body, .. }) => { + // Manually recurse over closures, because they are nested bodies + // that share the parent environment. We handle const blocks in + // `visit_inline_const`. + hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let body = visitor.tcx.hir().body(body); visitor.visit_body(body); } @@ -554,7 +554,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h } fn resolve_local<'tcx>( - visitor: &mut RegionResolutionVisitor<'tcx>, + visitor: &mut ScopeResolutionVisitor<'tcx>, pat: Option<&'tcx hir::Pat<'tcx>>, init: Option<&'tcx hir::Expr<'tcx>>, ) { @@ -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,14 +695,16 @@ 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, _), ..) | PatKind::Wild | PatKind::Never | PatKind::Path(_) - | PatKind::Lit(_) + | PatKind::Expr(_) | PatKind::Range(_, _, _) | PatKind::Err(_) => false, } @@ -722,7 +725,7 @@ fn resolve_local<'tcx>( /// | ( E& ) /// ``` fn record_rvalue_scope_if_borrow_expr<'tcx>( - visitor: &mut RegionResolutionVisitor<'tcx>, + visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &hir::Expr<'_>, blk_id: Option, ) { @@ -779,7 +782,7 @@ fn resolve_local<'tcx>( } } -impl<'tcx> RegionResolutionVisitor<'tcx> { +impl<'tcx> ScopeResolutionVisitor<'tcx> { /// Records the current parent (if any) as the parent of `child_scope`. /// Returns the depth of `child_scope`. fn record_child_scope(&mut self, child_scope: Scope) -> ScopeDepth { @@ -835,7 +838,7 @@ impl<'tcx> RegionResolutionVisitor<'tcx> { } } -impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { +impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> { fn visit_block(&mut self, b: &'tcx Block<'tcx>) { resolve_block(self, b); } @@ -903,6 +906,10 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) { resolve_local(self, Some(l.pat), l.init) } + fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { + let body = self.tcx.hir().body(c.body); + self.visit_body(body); + } } /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; @@ -919,7 +926,7 @@ pub(crate) fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { } let scope_tree = if let Some(body) = tcx.hir().maybe_body_owned_by(def_id.expect_local()) { - let mut visitor = RegionResolutionVisitor { + let mut visitor = ScopeResolutionVisitor { tcx, scope_tree: ScopeTree::default(), expr_and_pat_count: 0, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 81a5e9ee90d7..dd6adb17c5e4 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1120,7 +1120,7 @@ fn check_type_defn<'tcx>( } else { // Evaluate the constant proactively, to emit an error if the constant has // an unconditional error. We only do so if the const has no type params. - let _ = tcx.const_eval_poly(def_id.into()); + let _ = tcx.const_eval_poly(def_id); } } let field_id = field.did.expect_local(); diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 0c19e2e4c518..1569c0963c84 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -1036,7 +1036,7 @@ pub(super) fn const_conditions<'tcx>( icx.lowerer().lower_bounds( tcx.types.self_param, - supertraits.into_iter(), + supertraits, &mut bounds, ty::List::empty(), PredicateFilter::ConstIfConst, diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index c933095fd3db..d1a1e36c1d5a 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -1,4 +1,3 @@ -use rustc_errors::StashKey; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; @@ -45,7 +44,7 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type( if !hidden.ty.references_error() { for concrete_type in locator.typeck_types { if concrete_type.ty != tcx.erase_regions(hidden.ty) { - if let Ok(d) = hidden.build_mismatch_error(&concrete_type, def_id, tcx) { + if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) { d.emit(); } } @@ -121,7 +120,7 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local if !hidden.ty.references_error() { for concrete_type in locator.typeck_types { if concrete_type.ty != tcx.erase_regions(hidden.ty) { - if let Ok(d) = hidden.build_mismatch_error(&concrete_type, def_id, tcx) { + if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) { d.emit(); } } @@ -285,9 +284,8 @@ impl TaitConstraintLocator<'_> { debug!(?concrete_type, "found constraint"); if let Some(prev) = &mut self.found { if concrete_type.ty != prev.ty { - let (Ok(guar) | Err(guar)) = prev - .build_mismatch_error(&concrete_type, self.def_id, self.tcx) - .map(|d| d.emit()); + let (Ok(guar) | Err(guar)) = + prev.build_mismatch_error(&concrete_type, self.tcx).map(|d| d.emit()); prev.ty = Ty::new_error(self.tcx, guar); } } else { @@ -361,11 +359,8 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>( ); if let Some(prev) = &mut hir_opaque_ty { if concrete_type.ty != prev.ty { - if let Ok(d) = prev.build_mismatch_error(&concrete_type, def_id, tcx) { - d.stash( - tcx.def_span(opaque_type_key.def_id), - StashKey::OpaqueHiddenTypeMismatch, - ); + if let Ok(d) = prev.build_mismatch_error(&concrete_type, tcx) { + d.emit(); } } } else { @@ -435,9 +430,7 @@ impl RpitConstraintChecker<'_> { debug!(?concrete_type, "found constraint"); if concrete_type.ty != self.found.ty { - if let Ok(d) = - self.found.build_mismatch_error(&concrete_type, self.def_id, self.tcx) - { + if let Ok(d) = self.found.build_mismatch_error(&concrete_type, self.tcx) { d.emit(); } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index b7d3617fbe73..a1f2b8c75948 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -2,10 +2,12 @@ use rustc_ast::TraitObjectSyntax; use rustc_errors::codes::*; use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, StashKey, Suggestions}; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::DefId; use rustc_lint_defs::Applicability; use rustc_lint_defs::builtin::BARE_TRAIT_OBJECTS; use rustc_span::Span; +use rustc_span::edit_distance::find_best_match_for_name; use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use super::HirTyLowerer; @@ -86,7 +88,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Check if the impl trait that we are considering is an impl of a local trait. self.maybe_suggest_blanket_trait_impl(self_ty, &mut diag); self.maybe_suggest_assoc_ty_bound(self_ty, &mut diag); - // In case there is an associate type with the same name + self.maybe_suggest_typoed_method( + self_ty, + poly_trait_ref.trait_ref.trait_def_id(), + &mut diag, + ); + // In case there is an associated type with the same name // Add the suggestion to this error if let Some(mut sugg) = tcx.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion) @@ -96,7 +103,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { s1.append(s2); sugg.cancel(); } - diag.stash(self_ty.span, StashKey::TraitMissingMethod) + Some(diag.emit()) } else { tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, |lint| { lint.primary_message("trait objects without an explicit `dyn` are deprecated"); @@ -343,4 +350,44 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } } + + fn maybe_suggest_typoed_method( + &self, + self_ty: &hir::Ty<'_>, + trait_def_id: Option, + diag: &mut Diag<'_>, + ) { + let tcx = self.tcx(); + let Some(trait_def_id) = trait_def_id else { + return; + }; + let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::TypeRelative(path_ty, segment)), + .. + }) = tcx.parent_hir_node(self_ty.hir_id) + else { + return; + }; + if path_ty.hir_id != self_ty.hir_id { + return; + } + let names: Vec<_> = tcx + .associated_items(trait_def_id) + .in_definition_order() + .filter(|assoc| assoc.kind.namespace() == Namespace::ValueNS) + .map(|cand| cand.name) + .collect(); + if let Some(typo) = find_best_match_for_name(&names, segment.ident.name, None) { + diag.span_suggestion_verbose( + segment.ident.span, + format!( + "you may have misspelled this associated item, causing `{}` \ + to be interpreted as a type rather than a trait", + tcx.item_name(trait_def_id), + ), + typo, + Applicability::MaybeIncorrect, + ); + } + } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 2154568c5126..cb90fff782fb 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -35,7 +35,7 @@ use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability::AllowUnstable; -use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::print::PrintPolyTraitRefExt as _; use rustc_middle::ty::{ @@ -2262,25 +2262,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { _ => None, }; - if let Some(lit_input) = lit_input { - // If an error occurred, ignore that it's a literal and leave reporting the error up to - // mir. - match tcx.at(expr.span).lit_to_const(lit_input) { - Ok(c) => return Some(c), - Err(_) if lit_input.ty.has_aliases() => { - // allow the `ty` to be an alias type, though we cannot handle it here - return None; - } - Err(e) => { - tcx.dcx().span_delayed_bug( - expr.span, - format!("try_lower_anon_const_lit: couldn't lit_to_const {e:?}"), - ); - } - } - } - - None + lit_input + // Allow the `ty` to be an alias type, though we cannot handle it here, we just go through + // the more expensive anon const code path. + .filter(|l| !l.ty.has_aliases()) + .map(|l| tcx.at(expr.span).lit_to_const(l)) } fn lower_delegation_ty(&self, idx: hir::InferDelegationKind) -> Ty<'tcx> { @@ -2449,44 +2435,39 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ty::new_error(tcx, err) } hir::PatKind::Range(start, end, include_end) => { - let expr_to_const = |expr: &'tcx hir::Expr<'tcx>| -> ty::Const<'tcx> { - let (expr, neg) = match expr.kind { - hir::ExprKind::Unary(hir::UnOp::Neg, negated) => { - (negated, Some((expr.hir_id, expr.span))) - } - _ => (expr, None), - }; - let (c, c_ty) = match &expr.kind { - hir::ExprKind::Lit(lit) => { + let expr_to_const = |expr: &'tcx hir::PatExpr<'tcx>| -> ty::Const<'tcx> { + let (c, c_ty) = match expr.kind { + hir::PatExprKind::Lit { lit, negated } => { let lit_input = - LitToConstInput { lit: &lit.node, ty, neg: neg.is_some() }; - let ct = match tcx.lit_to_const(lit_input) { - Ok(c) => c, - Err(LitToConstError::Reported(err)) => { - ty::Const::new_error(tcx, err) - } - Err(LitToConstError::TypeError) => todo!(), - }; + LitToConstInput { lit: &lit.node, ty, neg: negated }; + let ct = tcx.lit_to_const(lit_input); (ct, ty) } - hir::ExprKind::Path(hir::QPath::Resolved( + hir::PatExprKind::Path(hir::QPath::Resolved( _, path @ &hir::Path { res: Res::Def(DefKind::ConstParam, def_id), .. }, )) => { - let _ = self.prohibit_generic_args( + match self.prohibit_generic_args( path.segments.iter(), GenericsArgsErrExtend::Param(def_id), - ); - let ty = tcx - .type_of(def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"); - let ct = self.lower_const_param(def_id, expr.hir_id); - (ct, ty) + ) { + Ok(()) => { + let ty = tcx + .type_of(def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"); + let ct = self.lower_const_param(def_id, expr.hir_id); + (ct, ty) + } + Err(guar) => ( + ty::Const::new_error(tcx, guar), + Ty::new_error(tcx, guar), + ), + } } _ => { @@ -2497,9 +2478,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } }; self.record_ty(expr.hir_id, c_ty, expr.span); - if let Some((id, span)) = neg { - self.record_ty(id, c_ty, span); - } c }; 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..39b6823cf0e0 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -199,6 +199,7 @@ impl<'a> State<'a> { Node::OpaqueTy(o) => self.print_opaque_ty(o), Node::Pat(a) => self.print_pat(a), Node::PatField(a) => self.print_patfield(a), + Node::PatExpr(a) => self.print_pat_expr(a), Node::Arm(a) => self.print_arm(a), Node::Infer(_) => self.word("_"), Node::PreciseCapturingNonLifetimeArg(param) => self.print_ident(param.ident), @@ -1849,6 +1850,19 @@ impl<'a> State<'a> { } } + fn print_pat_expr(&mut self, expr: &hir::PatExpr<'_>) { + match &expr.kind { + hir::PatExprKind::Lit { lit, negated } => { + if *negated { + self.word("-"); + } + self.print_literal(lit); + } + hir::PatExprKind::ConstBlock(c) => self.print_inline_const(c), + hir::PatExprKind::Path(qpath) => self.print_qpath(qpath, true), + } + } + fn print_pat(&mut self, pat: &hir::Pat<'_>) { self.maybe_print_comment(pat.span.lo()); self.ann.pre(self, AnnNode::Pat(pat)); @@ -1966,17 +1980,17 @@ impl<'a> State<'a> { self.pclose(); } } - PatKind::Lit(e) => self.print_expr(e), + PatKind::Expr(e) => self.print_pat_expr(e), PatKind::Range(begin, end, end_kind) => { if let Some(expr) = begin { - self.print_expr(expr); + self.print_pat_expr(expr); } match end_kind { RangeEnd::Included => self.word("..."), RangeEnd::Excluded => self.word(".."), } if let Some(expr) = end { - self.print_expr(expr); + self.print_pat_expr(expr); } } PatKind::Slice(before, slice, after) => { @@ -1999,6 +2013,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/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index a93da52b2703..0f424a39840a 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -165,6 +165,8 @@ hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this ret hir_typeck_remove_semi_for_coerce_semi = the `match` is a statement because of this semicolon, consider removing it hir_typeck_remove_semi_for_coerce_suggestion = remove this semicolon +hir_typeck_replace_comma_with_semicolon = replace the comma with a semicolon to create {$descr} + hir_typeck_return_stmt_outside_of_fn_body = {$statement_kind} statement outside of function body .encl_body_label = the {$statement_kind} is part of this body... diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index bd26be11279e..9ebc7a4657e8 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -38,6 +38,7 @@ use std::ops::Deref; use rustc_abi::ExternAbi; +use rustc_attr_parsing::InlineAttr; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, struct_span_code_err}; use rustc_hir as hir; @@ -926,8 +927,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { return Err(TypeError::IntrinsicCast); } - // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396). + let fn_attrs = self.tcx.codegen_fn_attrs(def_id); + if matches!(fn_attrs.inline, InlineAttr::Force { .. }) { + return Err(TypeError::ForceInlineCast); + } + // Safe `#[target_feature]` functions are not assignable to safe fn pointers + // (RFC 2396). if b_hdr.safety.is_safe() && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() { @@ -1197,6 +1203,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Ok(prev_ty); } + let is_force_inline = |ty: Ty<'tcx>| { + if let ty::FnDef(did, _) = ty.kind() { + matches!(self.tcx.codegen_fn_attrs(did).inline, InlineAttr::Force { .. }) + } else { + false + } + }; + if is_force_inline(prev_ty) || is_force_inline(new_ty) { + return Err(TypeError::ForceInlineCast); + } + // Special-case that coercion alone cannot handle: // Function items or non-capturing closures of differing IDs or GenericArgs. let (a_sig, b_sig) = { diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index e51323fc5c85..56f7a2c1150a 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -30,7 +30,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if expr_ty == expected { return; } - self.annotate_alternative_method_deref(err, expr, error); self.explain_self_literal(err, expr, expected, expr_ty); @@ -39,6 +38,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_missing_unwrap_expect(err, expr, expected, expr_ty) || self.suggest_remove_last_method_call(err, expr, expected) || self.suggest_associated_const(err, expr, expected) + || self.suggest_semicolon_in_repeat_expr(err, expr, expr_ty) || self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr) || self.suggest_option_to_bool(err, expr, expr_ty, expected) || self.suggest_compatible_variants(err, expr, expected, expr_ty) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index ff09583cc65a..4eed2bc12388 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -846,3 +846,16 @@ pub(crate) struct PassFnItemToVariadicFunction { pub sugg_span: Span, pub replace: String, } + +#[derive(Subdiagnostic)] +#[suggestion( + hir_typeck_replace_comma_with_semicolon, + applicability = "machine-applicable", + style = "verbose", + code = "; " +)] +pub(crate) struct ReplaceCommaWithSemicolon { + #[primary_span] + pub comma_span: Span, + pub descr: &'static str, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index fb73985e3060..3bb518e7f971 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -430,6 +430,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | hir::Node::AssocItemConstraint(_) | hir::Node::TraitRef(_) | hir::Node::PatField(_) + | hir::Node::PatExpr(_) | hir::Node::LetStmt(_) | hir::Node::Synthetic | hir::Node::Err(_) @@ -456,6 +457,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. // @@ -481,7 +485,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | hir::PatKind::Box(_) | hir::PatKind::Ref(_, _) | hir::PatKind::Deref(_) - | hir::PatKind::Lit(_) + | hir::PatKind::Expr(_) | hir::PatKind::Range(_, _, _) | hir::PatKind::Slice(_, _, _) | hir::PatKind::Err(_) => true, @@ -834,7 +838,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We always require that the type provided as the value for // a type parameter outlives the moment of instantiation. let args = self.typeck_results.borrow().node_args(expr.hir_id); - self.add_wf_bounds(args, expr); + self.add_wf_bounds(args, expr.span); ty } @@ -1793,7 +1797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_expr_const_block( + pub(super) fn check_expr_const_block( &self, block: &'tcx hir::ConstBlock, expected: Expectation<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index ecbae6ac72fa..1f48b703e4ac 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -595,7 +595,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx let place_ty = place.place.ty(); needs_to_be_read |= self.is_multivariant_adt(place_ty, pat.span); } - PatKind::Lit(_) | PatKind::Range(..) => { + PatKind::Expr(_) | PatKind::Range(..) => { // If the PatKind is a Lit or a Range then we want // to borrow discr. needs_to_be_read = true; @@ -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)?; } @@ -1802,7 +1803,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx PatKind::Path(_) | PatKind::Binding(.., None) - | PatKind::Lit(..) + | PatKind::Expr(..) | PatKind::Range(..) | PatKind::Never | PatKind::Wild diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 2f6e50c80149..be6d9570e35b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -3,7 +3,7 @@ use std::slice; use rustc_abi::FieldIdx; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey}; +use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -577,11 +577,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Registers obligations that all `args` are well-formed. - pub(crate) fn add_wf_bounds(&self, args: GenericArgsRef<'tcx>, expr: &hir::Expr<'_>) { + pub(crate) fn add_wf_bounds(&self, args: GenericArgsRef<'tcx>, span: Span) { for arg in args.iter().filter(|arg| { matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) }) { - self.register_wf_obligation(arg, expr.span, ObligationCauseCode::WellFormed(None)); + self.register_wf_obligation(arg, span, ObligationCauseCode::WellFormed(None)); } } @@ -806,17 +806,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let item_name = item_segment.ident; let result = self .resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id) - .map(|r| { - // lint bare trait if the method is found in the trait - if span.edition().at_least_rust_2021() { - self.dcx().try_steal_modify_and_emit_err( - qself.span, - StashKey::TraitMissingMethod, - |_err| {}, - ); - } - r - }) .or_else(|error| { let guar = self .dcx() @@ -840,17 +829,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - // Emit the diagnostic for bare traits. (We used to cancel for slightly better - // error messages, but cancelling stashed diagnostics is no longer allowed because - // it causes problems when tracking whether errors have actually occurred.) - if span.edition().at_least_rust_2021() { - self.dcx().try_steal_modify_and_emit_err( - qself.span, - StashKey::TraitMissingMethod, - |_err| {}, - ); - } - if item_name.name != kw::Empty { self.report_method_error( hir_id, @@ -1039,6 +1017,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { def_id, span, ), + Res::Err => { + return ( + Ty::new_error( + tcx, + tcx.dcx().span_delayed_bug(span, "could not resolve path {:?}"), + ), + res, + ); + } _ => bug!("instantiate_value_path on {:?}", res), }; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 3756f6339a45..39511ca30e0c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1320,14 +1320,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span = expr.span.shrink_to_hi(); let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) { errors::OptionResultRefMismatch::Copied { span, def_path } - } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() - && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( - self, - self.param_env, - ty, - clone_did, - ) - { + } else if self.type_is_clone_modulo_regions(self.param_env, ty) { errors::OptionResultRefMismatch::Cloned { span, def_path } } else { return false; @@ -2182,6 +2175,87 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Suggest replacing comma with semicolon in incorrect repeat expressions + /// like `["_", 10]` or `vec![String::new(), 10]`. + pub(crate) fn suggest_semicolon_in_repeat_expr( + &self, + err: &mut Diag<'_>, + expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + ) -> bool { + // Check if `expr` is contained in array of two elements + if let hir::Node::Expr(array_expr) = self.tcx.parent_hir_node(expr.hir_id) + && let hir::ExprKind::Array(elements) = array_expr.kind + && let [first, second] = &elements[..] + && second.hir_id == expr.hir_id + { + // Span between the two elements of the array + let comma_span = first.span.between(second.span); + + // Check if `expr` is a constant value of type `usize`. + // This can only detect const variable declarations and + // calls to const functions. + + // Checking this here instead of rustc_hir::hir because + // this check needs access to `self.tcx` but rustc_hir + // has no access to `TyCtxt`. + let expr_is_const_usize = expr_ty.is_usize() + && match expr.kind { + ExprKind::Path(QPath::Resolved( + None, + Path { res: Res::Def(DefKind::Const, _), .. }, + )) => true, + ExprKind::Call( + Expr { + kind: + ExprKind::Path(QPath::Resolved( + None, + Path { res: Res::Def(DefKind::Fn, fn_def_id), .. }, + )), + .. + }, + _, + ) => self.tcx.is_const_fn(*fn_def_id), + _ => false, + }; + + // Type of the first element is guaranteed to be checked + // when execution reaches here because `mismatched types` + // error occurs only when type of second element of array + // is not the same as type of first element. + let first_ty = self.typeck_results.borrow().expr_ty(first); + + // `array_expr` is from a macro `vec!["a", 10]` if + // 1. array expression's span is imported from a macro + // 2. first element of array implements `Clone` trait + // 3. second element is an integer literal or is an expression of `usize` like type + if self.tcx.sess.source_map().is_imported(array_expr.span) + && self.type_is_clone_modulo_regions(self.param_env, first_ty) + && (expr.is_size_lit() || expr_ty.is_usize_like()) + { + err.subdiagnostic(errors::ReplaceCommaWithSemicolon { + comma_span, + descr: "a vector", + }); + return true; + } + + // `array_expr` is from an array `["a", 10]` if + // 1. first element of array implements `Copy` trait + // 2. second element is an integer literal or is a const value of type `usize` + if self.type_is_copy_modulo_regions(self.param_env, first_ty) + && (expr.is_size_lit() || expr_is_const_usize) + { + err.subdiagnostic(errors::ReplaceCommaWithSemicolon { + comma_span, + descr: "an array", + }); + return true; + } + } + false + } + /// If the expected type is an enum (Issue #55250) with any variants whose /// sole field is of the found type, suggest such variants. (Issue #42764) pub(crate) fn suggest_compatible_variants( 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/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 0c93c9817b46..f549ced9dc33 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -611,7 +611,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // this is a projection from a trait reference, so we have to // make sure that the trait reference inputs are well-formed. - self.add_wf_bounds(all_args, self.call_expr); + self.add_wf_bounds(all_args, self.call_expr.span); // the function type must also be well-formed (this is not // implied by the args being well-formed because of inherent diff --git a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs index 5ccfcf93f69e..69d7a6c97cb3 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs @@ -8,7 +8,7 @@ use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; use rustc_middle::span_bug; use rustc_middle::ty::{self, Ty}; use rustc_session::lint::builtin::{RUST_2021_PRELUDE_COLLISIONS, RUST_2024_PRELUDE_COLLISIONS}; -use rustc_span::{Ident, Span, kw, sym}; +use rustc_span::{Ident, STDLIB_STABLE_CRATES, Span, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; @@ -76,7 +76,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // No need to lint if method came from std/core, as that will now be in the prelude - if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { + if STDLIB_STABLE_CRATES.contains(&self.tcx.crate_name(pick.item.def_id.krate)) { return; } @@ -252,7 +252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // No need to lint if method came from std/core, as that will now be in the prelude - if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { + if STDLIB_STABLE_CRATES.contains(&self.tcx.crate_name(pick.item.def_id.krate)) { return; } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 0b008fd10b50..b3d87ef4ad2b 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; @@ -170,6 +170,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, .. }) + | hir::Node::PatExpr(&hir::PatExpr { + kind: hir::PatExprKind::Path(QPath::TypeRelative(rcvr, segment)), + span, + .. + }) | hir::Node::Pat(&hir::Pat { kind: hir::PatKind::Path(QPath::TypeRelative(rcvr, segment)) @@ -690,6 +695,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) { @@ -1067,7 +1096,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) ) { continue; - }; + } match self.tcx.hir().get_if_local(item_def_id) { // Unmet obligation comes from a `derive` macro, point at it once to @@ -1181,8 +1210,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); entry.2.push(p); } - Some(node) => unreachable!("encountered `{node:?}` due to `{cause:#?}`"), - None => (), + _ => { + // It's possible to use well-formedness clauses to get obligations + // which point arbitrary items like ADTs, so there's no use in ICEing + // here if we find that the obligation originates from some other + // node that we don't handle. + } } } let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 98b28240f4cb..36094657eafd 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -30,6 +30,7 @@ use tracing::{debug, instrument, trace}; use ty::VariantDef; use super::report_unexpected_variant_res; +use crate::expectation::Expectation; use crate::gather_locals::DeclOrigin; use crate::{FnCtxt, LoweredTy, errors}; @@ -270,7 +271,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Wild | PatKind::Err(_) => expected, // We allow any type here; we ensure that the type is uninhabited during match checking. PatKind::Never => expected, - PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti), + PatKind::Expr(lt) => self.check_pat_lit(pat.span, lt, expected, ti), PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti), PatKind::Binding(ba, var_id, ident, sub) => { self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info) @@ -279,11 +280,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info) } PatKind::Path(ref qpath) => { - self.check_pat_path(pat, qpath, path_res.unwrap(), expected, ti) + self.check_pat_path(pat.hir_id, pat.span, qpath, path_res.unwrap(), expected, ti) } 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); @@ -393,7 +399,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`. // // Call `resolve_vars_if_possible` here for inline const blocks. - PatKind::Lit(lt) => match self.resolve_vars_if_possible(self.check_expr(lt)).kind() { + PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() { ty::Ref(..) => AdjustMode::Pass, _ => AdjustMode::Peel, }, @@ -422,7 +428,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, } } @@ -486,10 +494,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (expected, def_br, max_ref_mutbl) } + fn check_pat_expr_unadjusted(&self, lt: &'tcx hir::PatExpr<'tcx>) -> Ty<'tcx> { + let ty = match <.kind { + rustc_hir::PatExprKind::Lit { lit, .. } => { + self.check_expr_lit(lit, Expectation::NoExpectation) + } + rustc_hir::PatExprKind::ConstBlock(c) => { + self.check_expr_const_block(c, Expectation::NoExpectation) + } + rustc_hir::PatExprKind::Path(qpath) => { + let (res, opt_ty, segments) = + self.resolve_ty_and_res_fully_qualified_call(qpath, lt.hir_id, lt.span); + self.instantiate_value_path(segments, opt_ty, res, lt.span, lt.span, lt.hir_id).0 + } + }; + self.write_ty(lt.hir_id, ty); + ty + } + fn check_pat_lit( &self, span: Span, - lt: &hir::Expr<'tcx>, + lt: &hir::PatExpr<'tcx>, expected: Ty<'tcx>, ti: &TopInfo<'tcx>, ) -> Ty<'tcx> { @@ -500,7 +526,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Byte string patterns behave the same way as array patterns // They can denote both statically and dynamically-sized byte arrays. let mut pat_ty = ty; - if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(..), .. }) = lt.kind { + if let hir::PatExprKind::Lit { + lit: Spanned { node: ast::LitKind::ByteStr(..), .. }, .. + } = lt.kind + { let expected = self.structurally_resolve_type(span, expected); if let ty::Ref(_, inner_ty, _) = *expected.kind() && self.try_structurally_resolve_type(span, inner_ty).is_slice() @@ -517,7 +546,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if self.tcx.features().string_deref_patterns() - && let hir::ExprKind::Lit(Spanned { node: ast::LitKind::Str(..), .. }) = lt.kind + && let hir::PatExprKind::Lit { + lit: Spanned { node: ast::LitKind::Str(..), .. }, .. + } = lt.kind { let tcx = self.tcx; let expected = self.resolve_vars_if_possible(expected); @@ -558,15 +589,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_range( &self, span: Span, - lhs: Option<&'tcx hir::Expr<'tcx>>, - rhs: Option<&'tcx hir::Expr<'tcx>>, + lhs: Option<&'tcx hir::PatExpr<'tcx>>, + rhs: Option<&'tcx hir::PatExpr<'tcx>>, expected: Ty<'tcx>, ti: &TopInfo<'tcx>, ) -> Ty<'tcx> { - let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr { + let calc_side = |opt_expr: Option<&'tcx hir::PatExpr<'tcx>>| match opt_expr { None => None, Some(expr) => { - let ty = self.check_expr(expr); + let ty = self.check_pat_expr_unadjusted(expr); // Check that the end-point is possibly of numeric or char type. // The early check here is not for correctness, but rather better // diagnostics (e.g. when `&str` is being matched, `expected` will @@ -901,6 +932,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Or(..) + | PatKind::Guard(..) | PatKind::Tuple(..) | PatKind::Slice(..) => "binding", @@ -911,7 +943,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | PatKind::Box(..) | PatKind::Deref(_) | PatKind::Ref(..) - | PatKind::Lit(..) + | PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) => break 'block None, }, @@ -1045,7 +1077,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_path( &self, - pat: &Pat<'tcx>, + hir_id: HirId, + span: Span, qpath: &hir::QPath<'_>, path_resolution: (Res, Option>, &'tcx [hir::PathSegment<'tcx>]), expected: Ty<'tcx>, @@ -1064,8 +1097,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { let expected = "unit struct, unit variant or constant"; - let e = - report_unexpected_variant_res(tcx, res, None, qpath, pat.span, E0533, expected); + let e = report_unexpected_variant_res(tcx, res, None, qpath, span, E0533, expected); return Ty::new_error(tcx, e); } Res::SelfCtor(def_id) => { @@ -1080,7 +1112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { res, None, qpath, - pat.span, + span, E0533, "unit struct", ); @@ -1099,11 +1131,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check the path. let (pat_ty, pat_res) = - self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.span, pat.hir_id); + self.instantiate_value_path(segments, opt_ty, res, span, span, hir_id); if let Err(err) = - self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty) + self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, pat_ty) { - self.emit_bad_pat_path(err, pat, res, pat_res, pat_ty, segments); + self.emit_bad_pat_path(err, hir_id, span, res, pat_res, pat_ty, segments); } pat_ty } @@ -1146,13 +1178,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn emit_bad_pat_path( &self, mut e: Diag<'_>, - pat: &hir::Pat<'tcx>, + hir_id: HirId, + pat_span: Span, res: Res, pat_res: Res, pat_ty: Ty<'tcx>, segments: &'tcx [hir::PathSegment<'tcx>], ) { - let pat_span = pat.span; if let Some(span) = self.tcx.hir().res_span(pat_res) { e.span_label(span, format!("{} defined here", res.descr())); if let [hir::PathSegment { ident, .. }] = &*segments { @@ -1165,7 +1197,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { res.descr(), ), ); - match self.tcx.parent_hir_node(pat.hir_id) { + match self.tcx.parent_hir_node(hir_id) { hir::Node::PatField(..) => { e.span_suggestion_verbose( ident.span.shrink_to_hi(), @@ -1805,9 +1837,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } else if inexistent_fields.len() == 1 { match pat_field.pat.kind { - PatKind::Lit(expr) + PatKind::Expr(_) if !self.may_coerce( - self.typeck_results.borrow().expr_ty(expr), + self.typeck_results.borrow().node_type(pat_field.pat.hir_id), self.field_ty(field.span, field_def, args), ) => {} _ => { diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 53993ba8d6ca..cac891c4e4c4 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -147,15 +147,16 @@ impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { self.visit_body(body); self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, capture_clause); } - hir::ExprKind::ConstBlock(anon_const) => { - let body = self.fcx.tcx.hir().body(anon_const.body); - self.visit_body(body); - } _ => {} } intravisit::walk_expr(self, expr); } + + fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { + let body = self.fcx.tcx.hir().body(c.body); + self.visit_body(body); + } } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 5612aa75aae4..7c5838db5866 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -5,7 +5,7 @@ use std::mem; use rustc_data_structures::unord::ExtendUnord; -use rustc_errors::{ErrorGuaranteed, StashKey}; +use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; @@ -246,6 +246,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } } + + fn visit_const_block(&mut self, span: Span, anon_const: &hir::ConstBlock) { + self.visit_node_id(span, anon_const.hir_id); + + let body = self.tcx().hir().body(anon_const.body); + self.visit_body(body); + } } /////////////////////////////////////////////////////////////////////////// @@ -275,11 +282,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => { self.visit_field_id(e.hir_id); } - hir::ExprKind::ConstBlock(anon_const) => { - self.visit_node_id(e.span, anon_const.hir_id); - - let body = self.tcx().hir().body(anon_const.body); - self.visit_body(body); + hir::ExprKind::ConstBlock(ref anon_const) => { + self.visit_const_block(e.span, anon_const); } _ => {} } @@ -335,6 +339,14 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { intravisit::walk_pat(self, p); } + fn visit_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) { + self.visit_node_id(expr.span, expr.hir_id); + if let hir::PatExprKind::ConstBlock(c) = &expr.kind { + self.visit_const_block(expr.span, c); + } + intravisit::walk_pat_expr(self, expr); + } + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { intravisit::walk_local(self, l); let var_ty = self.fcx.local_ty(l.span, l.hir_id); @@ -570,15 +582,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { && last_opaque_ty.ty != hidden_type.ty { assert!(!self.fcx.next_trait_solver()); - if let Ok(d) = hidden_type.build_mismatch_error( - &last_opaque_ty, - opaque_type_key.def_id, - self.tcx(), - ) { - d.stash( - self.tcx().def_span(opaque_type_key.def_id), - StashKey::OpaqueHiddenTypeMismatch, - ); + if let Ok(d) = hidden_type.build_mismatch_error(&last_opaque_ty, self.tcx()) { + d.emit(); } } } diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 38e2dbbde7d0..d93707b745d4 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -97,7 +97,13 @@ macro_rules! bit_relations_inherent_impls { /// A fixed-size bitset type with a dense representation. /// -/// NOTE: Use [`GrowableBitSet`] if you need support for resizing after creation. +/// Note 1: Since this bitset is dense, if your domain is big, and/or relatively +/// homogeneous (for example, with long runs of bits set or unset), then it may +/// be preferable to instead use a [MixedBitSet], or an +/// [IntervalSet](crate::interval::IntervalSet). They should be more suited to +/// sparse, or highly-compressible, domains. +/// +/// Note 2: Use [`GrowableBitSet`] if you need support for resizing after creation. /// /// `T` is an index type, typically a newtyped `usize` wrapper, but it can also /// just be `usize`. @@ -108,33 +114,33 @@ macro_rules! bit_relations_inherent_impls { /// #[cfg_attr(feature = "nightly", derive(Decodable_Generic, Encodable_Generic))] #[derive(Eq, PartialEq, Hash)] -pub struct BitSet { +pub struct DenseBitSet { domain_size: usize, words: SmallVec<[Word; 2]>, marker: PhantomData, } -impl BitSet { +impl DenseBitSet { /// Gets the domain size. pub fn domain_size(&self) -> usize { self.domain_size } } -impl BitSet { +impl DenseBitSet { /// Creates a new, empty bitset with a given `domain_size`. #[inline] - pub fn new_empty(domain_size: usize) -> BitSet { + pub fn new_empty(domain_size: usize) -> DenseBitSet { let num_words = num_words(domain_size); - BitSet { domain_size, words: smallvec![0; num_words], marker: PhantomData } + DenseBitSet { domain_size, words: smallvec![0; num_words], marker: PhantomData } } /// Creates a new, filled bitset with a given `domain_size`. #[inline] - pub fn new_filled(domain_size: usize) -> BitSet { + pub fn new_filled(domain_size: usize) -> DenseBitSet { let num_words = num_words(domain_size); let mut result = - BitSet { domain_size, words: smallvec![!0; num_words], marker: PhantomData }; + DenseBitSet { domain_size, words: smallvec![!0; num_words], marker: PhantomData }; result.clear_excess_bits(); result } @@ -165,7 +171,7 @@ impl BitSet { /// Is `self` is a (non-strict) superset of `other`? #[inline] - pub fn superset(&self, other: &BitSet) -> bool { + pub fn superset(&self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); self.words.iter().zip(&other.words).all(|(a, b)| (a & b) == *b) } @@ -278,32 +284,36 @@ impl BitSet { } // dense REL dense -impl BitRelations> for BitSet { - fn union(&mut self, other: &BitSet) -> bool { +impl BitRelations> for DenseBitSet { + fn union(&mut self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); bitwise(&mut self.words, &other.words, |a, b| a | b) } - fn subtract(&mut self, other: &BitSet) -> bool { + fn subtract(&mut self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); bitwise(&mut self.words, &other.words, |a, b| a & !b) } - fn intersect(&mut self, other: &BitSet) -> bool { + fn intersect(&mut self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); bitwise(&mut self.words, &other.words, |a, b| a & b) } } -impl From> for BitSet { +impl From> for DenseBitSet { fn from(bit_set: GrowableBitSet) -> Self { bit_set.bit_set } } -impl Clone for BitSet { +impl Clone for DenseBitSet { fn clone(&self) -> Self { - BitSet { domain_size: self.domain_size, words: self.words.clone(), marker: PhantomData } + DenseBitSet { + domain_size: self.domain_size, + words: self.words.clone(), + marker: PhantomData, + } } fn clone_from(&mut self, from: &Self) { @@ -312,13 +322,13 @@ impl Clone for BitSet { } } -impl fmt::Debug for BitSet { +impl fmt::Debug for DenseBitSet { fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { w.debug_list().entries(self.iter()).finish() } } -impl ToString for BitSet { +impl ToString for DenseBitSet { fn to_string(&self) -> String { let mut result = String::new(); let mut sep = '['; @@ -902,7 +912,7 @@ impl BitRelations> for ChunkedBitSet { } } -impl BitRelations> for BitSet { +impl BitRelations> for DenseBitSet { fn union(&mut self, other: &ChunkedBitSet) -> bool { sequential_update(|elem| self.insert(elem), other.iter()) } @@ -1114,10 +1124,10 @@ where false } -/// A bitset with a mixed representation, using `BitSet` for small and medium -/// bitsets, and `ChunkedBitSet` for large bitsets, i.e. those with enough bits -/// for at least two chunks. This is a good choice for many bitsets that can -/// have large domain sizes (e.g. 5000+). +/// A bitset with a mixed representation, using `DenseBitSet` for small and +/// medium bitsets, and `ChunkedBitSet` for large bitsets, i.e. those with +/// enough bits for at least two chunks. This is a good choice for many bitsets +/// that can have large domain sizes (e.g. 5000+). /// /// `T` is an index type, typically a newtyped `usize` wrapper, but it can also /// just be `usize`. @@ -1127,7 +1137,7 @@ where /// will panic if the bitsets have differing domain sizes. #[derive(PartialEq, Eq)] pub enum MixedBitSet { - Small(BitSet), + Small(DenseBitSet), Large(ChunkedBitSet), } @@ -1144,7 +1154,7 @@ impl MixedBitSet { #[inline] pub fn new_empty(domain_size: usize) -> MixedBitSet { if domain_size <= CHUNK_BITS { - MixedBitSet::Small(BitSet::new_empty(domain_size)) + MixedBitSet::Small(DenseBitSet::new_empty(domain_size)) } else { MixedBitSet::Large(ChunkedBitSet::new_empty(domain_size)) } @@ -1283,7 +1293,7 @@ impl<'a, T: Idx> Iterator for MixedBitIter<'a, T> { /// to or greater than the domain size. #[derive(Clone, Debug, PartialEq)] pub struct GrowableBitSet { - bit_set: BitSet, + bit_set: DenseBitSet, } impl Default for GrowableBitSet { @@ -1306,11 +1316,11 @@ impl GrowableBitSet { } pub fn new_empty() -> GrowableBitSet { - GrowableBitSet { bit_set: BitSet::new_empty(0) } + GrowableBitSet { bit_set: DenseBitSet::new_empty(0) } } pub fn with_capacity(capacity: usize) -> GrowableBitSet { - GrowableBitSet { bit_set: BitSet::new_empty(capacity) } + GrowableBitSet { bit_set: DenseBitSet::new_empty(capacity) } } /// Returns `true` if the set has changed. @@ -1349,8 +1359,8 @@ impl GrowableBitSet { } } -impl From> for GrowableBitSet { - fn from(bit_set: BitSet) -> Self { +impl From> for GrowableBitSet { + fn from(bit_set: DenseBitSet) -> Self { Self { bit_set } } } @@ -1386,7 +1396,7 @@ impl BitMatrix { } /// Creates a new matrix, with `row` used as the value for every row. - pub fn from_row_n(row: &BitSet, num_rows: usize) -> BitMatrix { + pub fn from_row_n(row: &DenseBitSet, num_rows: usize) -> BitMatrix { let num_columns = row.domain_size(); let words_per_row = num_words(num_columns); assert_eq!(words_per_row, row.words.len()); @@ -1484,7 +1494,7 @@ impl BitMatrix { /// Adds the bits from `with` to the bits from row `write`, and /// returns `true` if anything changed. - pub fn union_row_with(&mut self, with: &BitSet, write: R) -> bool { + pub fn union_row_with(&mut self, with: &DenseBitSet, write: R) -> bool { assert!(write.index() < self.num_rows); assert_eq!(with.domain_size(), self.num_columns); let (write_start, write_end) = self.range(write); @@ -1541,8 +1551,8 @@ impl fmt::Debug for BitMatrix { /// A fixed-column-size, variable-row-size 2D bit matrix with a moderately /// sparse representation. /// -/// Initially, every row has no explicit representation. If any bit within a -/// row is set, the entire row is instantiated as `Some()`. +/// Initially, every row has no explicit representation. If any bit within a row +/// is set, the entire row is instantiated as `Some()`. /// Furthermore, any previously uninstantiated rows prior to it will be /// instantiated as `None`. Those prior rows may themselves become fully /// instantiated later on if any of their bits are set. @@ -1556,7 +1566,7 @@ where C: Idx, { num_columns: usize, - rows: IndexVec>>, + rows: IndexVec>>, } impl SparseBitMatrix { @@ -1565,10 +1575,10 @@ impl SparseBitMatrix { Self { num_columns, rows: IndexVec::new() } } - fn ensure_row(&mut self, row: R) -> &mut BitSet { - // Instantiate any missing rows up to and including row `row` with an empty `BitSet`. - // Then replace row `row` with a full `BitSet` if necessary. - self.rows.get_or_insert_with(row, || BitSet::new_empty(self.num_columns)) + fn ensure_row(&mut self, row: R) -> &mut DenseBitSet { + // Instantiate any missing rows up to and including row `row` with an empty `DenseBitSet`. + // Then replace row `row` with a full `DenseBitSet` if necessary. + self.rows.get_or_insert_with(row, || DenseBitSet::new_empty(self.num_columns)) } /// Sets the cell at `(row, column)` to true. Put another way, insert @@ -1642,17 +1652,17 @@ impl SparseBitMatrix { self.row(row).into_iter().flat_map(|r| r.iter()) } - pub fn row(&self, row: R) -> Option<&BitSet> { + pub fn row(&self, row: R) -> Option<&DenseBitSet> { self.rows.get(row)?.as_ref() } - /// Intersects `row` with `set`. `set` can be either `BitSet` or + /// Intersects `row` with `set`. `set` can be either `DenseBitSet` or /// `ChunkedBitSet`. Has no effect if `row` does not exist. /// /// Returns true if the row was changed. pub fn intersect_row(&mut self, row: R, set: &Set) -> bool where - BitSet: BitRelations, + DenseBitSet: BitRelations, { match self.rows.get_mut(row) { Some(Some(row)) => row.intersect(set), @@ -1660,13 +1670,13 @@ impl SparseBitMatrix { } } - /// Subtracts `set` from `row`. `set` can be either `BitSet` or + /// Subtracts `set` from `row`. `set` can be either `DenseBitSet` or /// `ChunkedBitSet`. Has no effect if `row` does not exist. /// /// Returns true if the row was changed. pub fn subtract_row(&mut self, row: R, set: &Set) -> bool where - BitSet: BitRelations, + DenseBitSet: BitRelations, { match self.rows.get_mut(row) { Some(Some(row)) => row.subtract(set), @@ -1674,13 +1684,13 @@ impl SparseBitMatrix { } } - /// Unions `row` with `set`. `set` can be either `BitSet` or + /// Unions `row` with `set`. `set` can be either `DenseBitSet` or /// `ChunkedBitSet`. /// /// Returns true if the row was changed. pub fn union_row(&mut self, row: R, set: &Set) -> bool where - BitSet: BitRelations, + DenseBitSet: BitRelations, { self.ensure_row(row).union(set) } diff --git a/compiler/rustc_index/src/bit_set/tests.rs b/compiler/rustc_index/src/bit_set/tests.rs index f61423239793..0350740aa811 100644 --- a/compiler/rustc_index/src/bit_set/tests.rs +++ b/compiler/rustc_index/src/bit_set/tests.rs @@ -8,7 +8,7 @@ use test::Bencher; #[test] fn test_new_filled() { for i in 0..128 { - let idx_buf = BitSet::new_filled(i); + let idx_buf = DenseBitSet::new_filled(i); let elems: Vec = idx_buf.iter().collect(); let expected: Vec = (0..i).collect(); assert_eq!(elems, expected); @@ -17,7 +17,7 @@ fn test_new_filled() { #[test] fn bitset_iter_works() { - let mut bitset: BitSet = BitSet::new_empty(100); + let mut bitset: DenseBitSet = DenseBitSet::new_empty(100); bitset.insert(1); bitset.insert(10); bitset.insert(19); @@ -32,7 +32,7 @@ fn bitset_iter_works() { #[test] fn bitset_iter_works_2() { - let mut bitset: BitSet = BitSet::new_empty(320); + let mut bitset: DenseBitSet = DenseBitSet::new_empty(320); bitset.insert(0); bitset.insert(127); bitset.insert(191); @@ -43,25 +43,25 @@ fn bitset_iter_works_2() { #[test] fn bitset_clone_from() { - let mut a: BitSet = BitSet::new_empty(10); + let mut a: DenseBitSet = DenseBitSet::new_empty(10); a.insert(4); a.insert(7); a.insert(9); - let mut b = BitSet::new_empty(2); + let mut b = DenseBitSet::new_empty(2); b.clone_from(&a); assert_eq!(b.domain_size(), 10); assert_eq!(b.iter().collect::>(), [4, 7, 9]); - b.clone_from(&BitSet::new_empty(40)); + b.clone_from(&DenseBitSet::new_empty(40)); assert_eq!(b.domain_size(), 40); assert_eq!(b.iter().collect::>(), []); } #[test] fn union_two_sets() { - let mut set1: BitSet = BitSet::new_empty(65); - let mut set2: BitSet = BitSet::new_empty(65); + let mut set1: DenseBitSet = DenseBitSet::new_empty(65); + let mut set2: DenseBitSet = DenseBitSet::new_empty(65); assert!(set1.insert(3)); assert!(!set1.insert(3)); assert!(set2.insert(5)); @@ -268,8 +268,8 @@ fn with_elements_chunked(elements: &[usize], domain_size: usize) -> ChunkedBitSe s } -fn with_elements_standard(elements: &[usize], domain_size: usize) -> BitSet { - let mut s = BitSet::new_empty(domain_size); +fn with_elements_standard(elements: &[usize], domain_size: usize) -> DenseBitSet { + let mut s = DenseBitSet::new_empty(domain_size); for &e in elements { assert!(s.insert(e)); } @@ -503,15 +503,15 @@ fn sparse_matrix_operations() { matrix.insert(2, 99); matrix.insert(4, 0); - let mut disjoint: BitSet = BitSet::new_empty(100); + let mut disjoint: DenseBitSet = DenseBitSet::new_empty(100); disjoint.insert(33); - let mut superset = BitSet::new_empty(100); + let mut superset = DenseBitSet::new_empty(100); superset.insert(22); superset.insert(75); superset.insert(33); - let mut subset = BitSet::new_empty(100); + let mut subset = DenseBitSet::new_empty(100); subset.insert(22); // SparseBitMatrix::remove @@ -568,7 +568,7 @@ fn dense_insert_range() { where R: RangeBounds + Clone + IntoIterator + std::fmt::Debug, { - let mut set = BitSet::new_empty(domain); + let mut set = DenseBitSet::new_empty(domain); set.insert_range(range.clone()); for i in set.iter() { assert!(range.contains(&i)); @@ -609,7 +609,7 @@ fn dense_insert_range() { #[test] fn dense_last_set_before() { - fn easy(set: &BitSet, needle: impl RangeBounds) -> Option { + fn easy(set: &DenseBitSet, needle: impl RangeBounds) -> Option { let mut last_leq = None; for e in set.iter() { if needle.contains(&e) { @@ -620,7 +620,7 @@ fn dense_last_set_before() { } #[track_caller] - fn cmp(set: &BitSet, needle: impl RangeBounds + Clone + std::fmt::Debug) { + fn cmp(set: &DenseBitSet, needle: impl RangeBounds + Clone + std::fmt::Debug) { assert_eq!( set.last_set_in(needle.clone()), easy(set, needle.clone()), @@ -629,7 +629,7 @@ fn dense_last_set_before() { set ); } - let mut set = BitSet::new_empty(300); + let mut set = DenseBitSet::new_empty(300); cmp(&set, 50..=50); set.insert(WORD_BITS); cmp(&set, WORD_BITS..=WORD_BITS); @@ -645,7 +645,7 @@ fn dense_last_set_before() { for i in 0..=WORD_BITS * 2 { for j in i..=WORD_BITS * 2 { for k in 0..WORD_BITS * 2 { - let mut set = BitSet::new_empty(300); + let mut set = DenseBitSet::new_empty(300); cmp(&set, i..j); cmp(&set, i..=j); set.insert(k); @@ -658,7 +658,7 @@ fn dense_last_set_before() { #[bench] fn bench_insert(b: &mut Bencher) { - let mut bs = BitSet::new_filled(99999usize); + let mut bs = DenseBitSet::new_filled(99999usize); b.iter(|| { black_box(bs.insert(black_box(100u32))); }); @@ -666,7 +666,7 @@ fn bench_insert(b: &mut Bencher) { #[bench] fn bench_remove(b: &mut Bencher) { - let mut bs = BitSet::new_filled(99999usize); + let mut bs = DenseBitSet::new_filled(99999usize); b.iter(|| { black_box(bs.remove(black_box(100u32))); }); @@ -674,7 +674,7 @@ fn bench_remove(b: &mut Bencher) { #[bench] fn bench_iter(b: &mut Bencher) { - let bs = BitSet::new_filled(99999usize); + let bs = DenseBitSet::new_filled(99999usize); b.iter(|| { bs.iter().map(|b: usize| black_box(b)).for_each(drop); }); @@ -682,8 +682,8 @@ fn bench_iter(b: &mut Bencher) { #[bench] fn bench_intersect(b: &mut Bencher) { - let mut ba: BitSet = BitSet::new_filled(99999usize); - let bb = BitSet::new_filled(99999usize); + let mut ba: DenseBitSet = DenseBitSet::new_filled(99999usize); + let bb = DenseBitSet::new_filled(99999usize); b.iter(|| { ba.intersect(black_box(&bb)); }); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 9ad690399140..07c4b8987216 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -24,6 +24,7 @@ use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, build_session, filesearc use rustc_span::edition::{DEFAULT_EDITION, Edition}; use rustc_span::source_map::{RealFileLoader, SourceMapInputs}; use rustc_span::{FileName, SourceFileHashAlgorithm, sym}; +use rustc_target::abi::Align; use rustc_target::spec::{ CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel, WasmCAbi, @@ -582,6 +583,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 +615,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 +783,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); @@ -806,6 +808,7 @@ fn test_unstable_options_tracking_hash() { tracked!(location_detail, LocationDetail { file: true, line: false, column: false }); tracked!(maximal_hir_to_mir_coverage, true); tracked!(merge_functions, Some(MergeFunctions::Disabled)); + tracked!(min_function_alignment, Some(Align::EIGHT)); tracked!(mir_emit_retag, true); tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]); tracked!(mir_keep_place_mention, true); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 2feed5e80dc5..fb3cf5afad03 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 /// @@ -1271,9 +1271,10 @@ declare_lint! { /// `pub(crate)` visibility is recommended to be used instead. This more clearly expresses the /// 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 - /// is desired for this to become warn-by-default. + /// This lint is "allow" by default because it will trigger for a large amount of existing Rust code. + /// 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" @@ -1302,9 +1303,9 @@ impl UnreachablePub { cx.effective_visibilities.effective_vis(def_id).map(|effective_vis| { effective_vis.at_level(rustc_middle::middle::privacy::Level::Reachable) }) - && let parent_parent = cx.tcx.parent_module_from_def_id( - cx.tcx.parent_module_from_def_id(def_id.into()).into(), - ) + && let parent_parent = cx + .tcx + .parent_module_from_def_id(cx.tcx.parent_module_from_def_id(def_id).into()) && *restricted_did == parent_parent.to_local_def_id() && !restricted_did.to_def_id().is_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/src/tests.rs b/compiler/rustc_lint/src/tests.rs index 186dec5904b4..f49301b0215d 100644 --- a/compiler/rustc_lint/src/tests.rs +++ b/compiler/rustc_lint/src/tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] +#![allow(rustc::symbol_intern_string_literal)] use rustc_span::{Symbol, create_default_session_globals_then}; diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 8b1526bc747d..3059adb3fda1 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1220,7 +1220,7 @@ impl EarlyLintPass for UnusedParens { // Do not lint on `(..)` as that will result in the other arms being useless. Paren(_) // The other cases do not contain sub-patterns. - | Wild | Never | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) | Err(_) => {}, + | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) | Err(_) => {}, // These are list-like patterns; parens can always be removed. TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { self.check_unused_parens_pat(cx, p, false, false, keep_space); 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_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index c8715f94d5dd..1ea075c2cb36 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -867,7 +867,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // First up we check for global allocators. Look at the crate graph here // and see what's a global allocator, including if we ourselves are a // global allocator. - #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + #[allow(rustc::symbol_intern_string_literal)] let this_crate = Symbol::intern("this crate"); let mut global_allocator = self.cstore.has_global_allocator.then_some(this_crate); diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index c2b5e318bda7..e02c4871f35b 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -871,7 +871,7 @@ impl MetadataBlob { let def_kind = root.tables.def_kind.get(blob, item).unwrap(); let def_key = root.tables.def_keys.get(blob, item).unwrap().decode(blob); - #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + #[allow(rustc::symbol_intern_string_literal)] let def_name = if item == CRATE_DEF_INDEX { kw::Crate } else { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4fe8f73efd60..4f9cdc9a474b 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -15,7 +15,7 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIndex, DefPathHash, Stable use rustc_hir::definitions::DefKey; use rustc_hir::lang_items::LangItem; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_macros::{ Decodable, Encodable, MetadataDecodable, MetadataEncodable, TyDecodable, TyEncodable, }; @@ -450,7 +450,7 @@ define_tables! { trait_item_def_id: Table, expn_that_defined: Table>, default_fields: Table>, - params_in_repr: Table>>, + params_in_repr: Table>>, repr_options: Table>, // `def_keys` and `def_path_hashes` represent a lazy version of a // `DefPathTable`. This allows us to avoid deserializing an entire 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/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index dee4c424387f..5d78bed5cf80 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -938,6 +938,7 @@ impl<'hir> Map<'hir> { Node::OpaqueTy(op) => op.span, Node::Pat(pat) => pat.span, Node::PatField(field) => field.span, + Node::PatExpr(lit) => lit.span, Node::Arm(arm) => arm.span, Node::Block(block) => block.span, Node::Ctor(..) => self.span_with_body(self.tcx.parent_hir_id(hir_id)), @@ -1209,6 +1210,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { Node::OpaqueTy(_) => node_str("opaque type"), Node::Pat(_) => node_str("pat"), Node::PatField(_) => node_str("pattern field"), + Node::PatExpr(_) => node_str("pattern literal"), Node::Param(_) => node_str("param"), Node::Arm(_) => node_str("arm"), Node::Block(_) => node_str("block"), @@ -1244,6 +1246,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod foreign_items, body_owners, opaques, + nested_bodies, .. } = collector; ModuleItems { @@ -1254,6 +1257,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod foreign_items: foreign_items.into_boxed_slice(), body_owners: body_owners.into_boxed_slice(), opaques: opaques.into_boxed_slice(), + nested_bodies: nested_bodies.into_boxed_slice(), } } @@ -1274,6 +1278,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { foreign_items, body_owners, opaques, + nested_bodies, .. } = collector; @@ -1285,6 +1290,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { foreign_items: foreign_items.into_boxed_slice(), body_owners: body_owners.into_boxed_slice(), opaques: opaques.into_boxed_slice(), + nested_bodies: nested_bodies.into_boxed_slice(), } } @@ -1300,6 +1306,7 @@ struct ItemCollector<'tcx> { foreign_items: Vec, body_owners: Vec, opaques: Vec, + nested_bodies: Vec, } impl<'tcx> ItemCollector<'tcx> { @@ -1314,6 +1321,7 @@ impl<'tcx> ItemCollector<'tcx> { foreign_items: Vec::default(), body_owners: Vec::default(), opaques: Vec::default(), + nested_bodies: Vec::default(), } } } @@ -1356,6 +1364,7 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { fn visit_inline_const(&mut self, c: &'hir ConstBlock) { self.body_owners.push(c.def_id); + self.nested_bodies.push(c.def_id); intravisit::walk_inline_const(self, c) } @@ -1367,6 +1376,7 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { fn visit_expr(&mut self, ex: &'hir Expr<'hir>) { if let ExprKind::Closure(closure) = ex.kind { self.body_owners.push(closure.def_id); + self.nested_bodies.push(closure.def_id); } intravisit::walk_expr(self, ex) } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index ffefd81cd08e..0d2acf96d08f 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -30,6 +30,7 @@ pub struct ModuleItems { foreign_items: Box<[ForeignItemId]>, opaques: Box<[LocalDefId]>, body_owners: Box<[LocalDefId]>, + nested_bodies: Box<[LocalDefId]>, } impl ModuleItems { @@ -70,6 +71,10 @@ impl ModuleItems { self.opaques.iter().copied() } + pub fn nested_bodies(&self) -> impl Iterator + '_ { + self.nested_bodies.iter().copied() + } + pub fn definitions(&self) -> impl Iterator + '_ { self.owners().map(|id| id.def_id) } 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/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 29f26180c973..cd6b2d65bf18 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter}; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::Span; @@ -303,8 +303,8 @@ pub struct MCDCDecisionSpan { /// Used by the `coverage_ids_info` query. #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)] pub struct CoverageIdsInfo { - pub counters_seen: BitSet, - pub zero_expressions: BitSet, + pub counters_seen: DenseBitSet, + pub zero_expressions: DenseBitSet, } impl CoverageIdsInfo { diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 8d73c9e76de1..b88137544bca 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -16,7 +16,6 @@ use rustc_abi::{AddressSpace, Align, Endian, HasDataLayout, Size}; use rustc_ast::{LitKind, Mutability}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lock; -use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; @@ -84,16 +83,6 @@ pub struct LitToConstInput<'tcx> { pub neg: bool, } -/// Error type for `tcx.lit_to_const`. -#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)] -pub enum LitToConstError { - /// The literal's inferred type did not match the expected `ty` in the input. - /// This is used for graceful error handling (`span_delayed_bug`) in - /// type checking (`Const::from_anon_const`). - TypeError, - Reported(ErrorGuaranteed), -} - #[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct AllocId(pub NonZero); diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 98ef7d58a502..bbb8bdce4a0c 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -21,7 +21,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_hir::{ self as hir, BindingMode, ByRef, CoroutineDesugaring, CoroutineKind, HirId, ImplicitSelfKind, }; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_serialize::{Decodable, Encodable}; diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 27168b2a9f24..111c3b6956a2 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -132,9 +132,10 @@ impl<'tcx> MonoItem<'tcx> { // creating one copy of this `#[inline]` function which may // conflict with upstream crates as it could be an exported // symbol. - match tcx.codegen_fn_attrs(instance.def_id()).inline { - InlineAttr::Always => InstantiationMode::LocalCopy, - _ => InstantiationMode::GloballyShared { may_conflict: true }, + if tcx.codegen_fn_attrs(instance.def_id()).inline.always() { + InstantiationMode::LocalCopy + } else { + InstantiationMode::GloballyShared { may_conflict: true } } } MonoItem::Static(..) | MonoItem::GlobalAsm(..) => { 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/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs index b8b74da401c9..0e7dcc24dafa 100644 --- a/compiler/rustc_middle/src/mir/traversal.rs +++ b/compiler/rustc_middle/src/mir/traversal.rs @@ -21,7 +21,7 @@ use super::*; #[derive(Clone)] pub struct Preorder<'a, 'tcx> { body: &'a Body<'tcx>, - visited: BitSet, + visited: DenseBitSet, worklist: Vec, root_is_start_block: bool, } @@ -32,7 +32,7 @@ impl<'a, 'tcx> Preorder<'a, 'tcx> { Preorder { body, - visited: BitSet::new_empty(body.basic_blocks.len()), + visited: DenseBitSet::new_empty(body.basic_blocks.len()), worklist, root_is_start_block: root == START_BLOCK, } @@ -106,7 +106,7 @@ impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> { /// A Postorder traversal of this graph is `D B C A` or `D C B A` pub struct Postorder<'a, 'tcx, C> { basic_blocks: &'a IndexSlice>, - visited: BitSet, + visited: DenseBitSet, visit_stack: Vec<(BasicBlock, Successors<'a>)>, root_is_start_block: bool, extra: C, @@ -123,7 +123,7 @@ where ) -> Postorder<'a, 'tcx, C> { let mut po = Postorder { basic_blocks, - visited: BitSet::new_empty(basic_blocks.len()), + visited: DenseBitSet::new_empty(basic_blocks.len()), visit_stack: Vec::new(), root_is_start_block: root == START_BLOCK, extra, @@ -285,8 +285,8 @@ pub fn reachable<'a, 'tcx>( preorder(body) } -/// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`. -pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet { +/// Returns a `DenseBitSet` containing all basic blocks reachable from the `START_BLOCK`. +pub fn reachable_as_bitset(body: &Body<'_>) -> DenseBitSet { let mut iter = preorder(body); while let Some(_) = iter.next() {} iter.visited @@ -340,13 +340,13 @@ pub fn mono_reachable<'a, 'tcx>( MonoReachable::new(body, tcx, instance) } -/// [`MonoReachable`] internally accumulates a [`BitSet`] of visited blocks. This is just a +/// [`MonoReachable`] internally accumulates a [`DenseBitSet`] of visited blocks. This is just a /// convenience function to run that traversal then extract its set of reached blocks. pub fn mono_reachable_as_bitset<'a, 'tcx>( body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, -) -> BitSet { +) -> DenseBitSet { let mut iter = mono_reachable(body, tcx, instance); while let Some(_) = iter.next() {} iter.visited @@ -356,11 +356,11 @@ pub struct MonoReachable<'a, 'tcx> { body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, - visited: BitSet, + visited: DenseBitSet, // Other traversers track their worklist in a Vec. But we don't care about order, so we can - // store ours in a BitSet and thus save allocations because BitSet has a small size + // store ours in a DenseBitSet and thus save allocations because DenseBitSet has a small size // optimization. - worklist: BitSet, + worklist: DenseBitSet, } impl<'a, 'tcx> MonoReachable<'a, 'tcx> { @@ -369,13 +369,13 @@ impl<'a, 'tcx> MonoReachable<'a, 'tcx> { tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, ) -> MonoReachable<'a, 'tcx> { - let mut worklist = BitSet::new_empty(body.basic_blocks.len()); + let mut worklist = DenseBitSet::new_empty(body.basic_blocks.len()); worklist.insert(START_BLOCK); MonoReachable { body, tcx, instance, - visited: BitSet::new_empty(body.basic_blocks.len()), + visited: DenseBitSet::new_empty(body.basic_blocks.len()), worklist, } } diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index b72c0e776fe9..1676afb4b6ec 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -141,14 +141,6 @@ impl EraseType for Result>, &ty::layout::Layou >()]; } -impl EraseType for Result, mir::interpret::LitToConstError> { - type Result = [u8; size_of::, mir::interpret::LitToConstError>>()]; -} - -impl EraseType for Result, mir::interpret::LitToConstError> { - type Result = [u8; size_of::, mir::interpret::LitToConstError>>()]; -} - impl EraseType for Result, mir::interpret::ErrorHandled> { type Result = [u8; size_of::, mir::interpret::ErrorHandled>>()]; } @@ -296,7 +288,6 @@ trivial! { rustc_middle::mir::interpret::AllocId, rustc_middle::mir::interpret::CtfeProvenance, rustc_middle::mir::interpret::ErrorHandled, - rustc_middle::mir::interpret::LitToConstError, rustc_middle::thir::ExprId, rustc_middle::traits::CodegenObligationError, rustc_middle::traits::EvaluationResult, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index c960f03cf061..7d334847d44b 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -57,7 +57,7 @@ use crate::middle::resolve_bound_vars::{ObjectLifetimeDefault, ResolveBoundVars, use crate::middle::stability::{self, DeprecationEntry}; use crate::mir::interpret::{ EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, - EvalToValTreeResult, GlobalId, LitToConstError, LitToConstInput, + EvalToValTreeResult, GlobalId, LitToConstInput, }; use crate::mir::mono::{CodegenUnit, CollectionMode, MonoItem}; use crate::query::erase::{Erase, erase, restore}; @@ -297,7 +297,7 @@ rustc_queries! { separate_provide_extern } - query unsizing_params_for_adt(key: DefId) -> &'tcx rustc_index::bit_set::BitSet + query unsizing_params_for_adt(key: DefId) -> &'tcx rustc_index::bit_set::DenseBitSet { arena_cache desc { |tcx| @@ -494,7 +494,7 @@ rustc_queries! { } /// Set of param indexes for type params that are in the type's representation - query params_in_repr(key: DefId) -> &'tcx rustc_index::bit_set::BitSet { + query params_in_repr(key: DefId) -> &'tcx rustc_index::bit_set::DenseBitSet { desc { "finding type parameters in the representation" } arena_cache no_hash @@ -1268,7 +1268,7 @@ rustc_queries! { // FIXME get rid of this with valtrees query lit_to_const( key: LitToConstInput<'tcx> - ) -> Result, LitToConstError> { + ) -> ty::Const<'tcx> { desc { "converting literal to const" } } @@ -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/context.rs b/compiler/rustc_middle/src/ty/context.rs index d26c007d227d..24f10b4fbe78 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -613,7 +613,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.coroutine_is_async_gen(coroutine_def_id) } - type UnsizingParams = &'tcx rustc_index::bit_set::BitSet; + type UnsizingParams = &'tcx rustc_index::bit_set::DenseBitSet; fn unsizing_params_for_adt(self, adt_def_id: DefId) -> Self::UnsizingParams { self.unsizing_params_for_adt(adt_def_id) } diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 714094db0530..35fbaa99569c 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -109,6 +109,9 @@ impl<'tcx> TypeError<'tcx> { TypeError::ConstMismatch(ref values) => { format!("expected `{}`, found `{}`", values.expected, values.found).into() } + TypeError::ForceInlineCast => { + "cannot coerce functions which must be inlined to function pointers".into() + } TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(), TypeError::TargetFeatureCast(_) => { "cannot coerce functions with `#[target_feature]` to safe function pointers".into() diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 6e6da6de749a..8d4d127607d3 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -770,6 +770,7 @@ where size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: tcx.data_layout.i8_align.abi, + randomization_seed: 0, }) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 5e929fbec0bf..bf0ccdc0f101 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -32,7 +32,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; -use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; +use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_hir::LangItem; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; @@ -782,18 +782,8 @@ impl<'tcx> OpaqueHiddenType<'tcx> { pub fn build_mismatch_error( &self, other: &Self, - opaque_def_id: LocalDefId, tcx: TyCtxt<'tcx>, ) -> Result, ErrorGuaranteed> { - // We used to cancel here for slightly better error messages, but - // cancelling stashed diagnostics is no longer allowed because it - // causes problems when tracking whether errors have actually - // occurred. - tcx.sess.dcx().try_steal_modify_and_emit_err( - tcx.def_span(opaque_def_id), - StashKey::OpaqueHiddenTypeMismatch, - |_err| {}, - ); (self.ty, other.ty).error_reported()?; // Found different concrete types for the opaque type. let sub_diag = if self.span == other.span { @@ -1417,8 +1407,8 @@ impl Hash for FieldDef { impl<'tcx> FieldDef { /// Returns the type of this field. The resulting type is not normalized. The `arg` is /// typically obtained via the second field of [`TyKind::Adt`]. - pub fn ty(&self, tcx: TyCtxt<'tcx>, arg: GenericArgsRef<'tcx>) -> Ty<'tcx> { - tcx.type_of(self.did).instantiate(tcx, arg) + pub fn ty(&self, tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { + tcx.type_of(self.did).instantiate(tcx, args) } /// Computes the `Ident` of this variant by looking up the `Span` diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index f611b69905c9..e86e01451fef 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -165,10 +165,14 @@ impl<'tcx> NormalizeAfterErasingRegionsFolder<'tcx> { arg: ty::GenericArg<'tcx>, ) -> ty::GenericArg<'tcx> { let arg = self.typing_env.as_query_input(arg); - self.tcx.try_normalize_generic_arg_after_erasing_regions(arg).unwrap_or_else(|_| bug!( - "Failed to normalize {:?}, maybe try to call `try_normalize_erasing_regions` instead", - arg.value - )) + self.tcx.try_normalize_generic_arg_after_erasing_regions(arg).unwrap_or_else(|_| { + bug!( + "Failed to normalize {:?} in typing_env={:?}, \ + maybe try to call `try_normalize_erasing_regions` instead", + arg.value, + self.typing_env, + ) + }) } } diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 86a95827e843..6b6c6f3c72fe 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -96,7 +96,7 @@ trivially_parameterized_over_tcx! { rustc_hir::def_id::DefIndex, rustc_hir::definitions::DefKey, rustc_hir::OpaqueTyOrigin, - rustc_index::bit_set::BitSet, + rustc_index::bit_set::DenseBitSet, rustc_index::bit_set::FiniteBitSet, rustc_session::cstore::ForeignModule, rustc_session::cstore::LinkagePreference, 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/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 92b3632c8acc..2980964898c3 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -27,7 +27,7 @@ use crate::infer::canonical::Canonical; use crate::ty::InferTy::*; use crate::ty::{ self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, - Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, + Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, }; // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here @@ -1017,6 +1017,18 @@ impl<'tcx> Ty<'tcx> { } } + /// Check if type is an `usize`. + #[inline] + pub fn is_usize(self) -> bool { + matches!(self.kind(), Uint(UintTy::Usize)) + } + + /// Check if type is an `usize` or an integral type variable. + #[inline] + pub fn is_usize_like(self) -> bool { + matches!(self.kind(), Uint(UintTy::Usize) | Infer(IntVar(_))) + } + #[inline] pub fn is_never(self) -> bool { matches!(self.kind(), Never) 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/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 119047227431..1f3689926bcf 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -12,6 +12,7 @@ rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 5d61a9d1e752..5203c33c968f 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -118,6 +118,12 @@ mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior .label = use of extern static +mir_build_force_inline = + `{$callee}` is incompatible with `#[rustc_force_inline]` + .attr = annotation here + .callee = `{$callee}` defined here + .note = incompatible due to: {$reason} + mir_build_inform_irrefutable = `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant mir_build_initializing_type_with_requires_unsafe = diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index 177c1e33a83b..e4e452aff755 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -3,13 +3,12 @@ use rustc_abi::Size; use rustc_ast as ast; use rustc_hir::LangItem; -use rustc_middle::mir::interpret::{ - Allocation, CTFE_ALLOC_SALT, LitToConstError, LitToConstInput, Scalar, -}; +use rustc_middle::mir::interpret::{Allocation, CTFE_ALLOC_SALT, LitToConstInput, Scalar}; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::{ - self, CanonicalUserType, CanonicalUserTypeAnnotation, Ty, TyCtxt, UserTypeAnnotationIndex, + self, CanonicalUserType, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt as _, + UserTypeAnnotationIndex, }; use rustc_middle::{bug, mir, span_bug}; use tracing::{instrument, trace}; @@ -50,16 +49,7 @@ pub(crate) fn as_constant_inner<'tcx>( let Expr { ty, temp_lifetime: _, span, ref kind } = *expr; match *kind { ExprKind::Literal { lit, neg } => { - let const_ = match lit_to_mir_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }) - { - Ok(c) => c, - Err(LitToConstError::Reported(guar)) => { - Const::Ty(Ty::new_error(tcx, guar), ty::Const::new_error(tcx, guar)) - } - Err(LitToConstError::TypeError) => { - bug!("encountered type error in `lit_to_mir_constant`") - } - }; + let const_ = lit_to_mir_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }); ConstOperand { span, user_ty: None, const_ } } @@ -108,11 +98,13 @@ pub(crate) fn as_constant_inner<'tcx>( } #[instrument(skip(tcx, lit_input))] -fn lit_to_mir_constant<'tcx>( - tcx: TyCtxt<'tcx>, - lit_input: LitToConstInput<'tcx>, -) -> Result, LitToConstError> { +fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>) -> Const<'tcx> { let LitToConstInput { lit, ty, neg } = lit_input; + + if let Err(guar) = ty.error_reported() { + return Const::Ty(Ty::new_error(tcx, guar), ty::Const::new_error(tcx, guar)); + } + let trunc = |n| { let width = match tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) { Ok(layout) => layout.size, @@ -123,7 +115,7 @@ fn lit_to_mir_constant<'tcx>( trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); let result = width.truncate(n); trace!("trunc result: {}", result); - Ok(ConstValue::Scalar(Scalar::from_uint(result, width))) + ConstValue::Scalar(Scalar::from_uint(result, width)) }; let value = match (lit, ty.kind()) { @@ -154,20 +146,18 @@ fn lit_to_mir_constant<'tcx>( ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1))) } (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { - trunc(if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() })? + trunc(if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() }) + } + (ast::LitKind::Float(n, _), ty::Float(fty)) => { + parse_float_into_constval(*n, *fty, neg).unwrap() } - (ast::LitKind::Float(n, _), ty::Float(fty)) => parse_float_into_constval(*n, *fty, neg) - .ok_or_else(|| { - LitToConstError::Reported( - tcx.dcx() - .delayed_bug(format!("couldn't parse float literal: {:?}", lit_input.lit)), - ) - })?, (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)), (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)), - (ast::LitKind::Err(guar), _) => return Err(LitToConstError::Reported(*guar)), - _ => return Err(LitToConstError::TypeError), + (ast::LitKind::Err(guar), _) => { + return Const::Ty(Ty::new_error(tcx, *guar), ty::Const::new_error(tcx, *guar)); + } + _ => bug!("invalid lit/ty combination in `lit_to_mir_constant`: {lit:?}: {ty:?}"), }; - Ok(Const::Val(value, ty)) + Const::Val(value, ty) } diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 932b6fbe026e..8b01ec0d06af 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -29,6 +29,7 @@ use rustc_span::{Span, Symbol, sym}; use super::lints; use crate::builder::expr::as_place::PlaceBuilder; use crate::builder::scope::DropKind; +use crate::check_inline; pub(crate) fn closure_saved_names_of_captured_variables<'tcx>( tcx: TyCtxt<'tcx>, @@ -80,6 +81,7 @@ pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx }; lints::check(tcx, &body); + check_inline::check_force_inline(tcx, &body); // The borrow checker will replace all the regions here with its own // inference variables. There's no point having non-erased regions here. 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/check_inline.rs b/compiler/rustc_mir_build/src/check_inline.rs new file mode 100644 index 000000000000..1af3b3e2c135 --- /dev/null +++ b/compiler/rustc_mir_build/src/check_inline.rs @@ -0,0 +1,81 @@ +use rustc_attr_parsing::InlineAttr; +use rustc_hir::def_id::DefId; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::{Body, TerminatorKind}; +use rustc_middle::ty; +use rustc_middle::ty::TyCtxt; +use rustc_span::sym; + +/// Check that a body annotated with `#[rustc_force_inline]` will not fail to inline based on its +/// definition alone (irrespective of any specific caller). +pub(crate) fn check_force_inline<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let def_id = body.source.def_id(); + if !tcx.hir().body_owner_kind(def_id).is_fn_or_closure() || !def_id.is_local() { + return; + } + let InlineAttr::Force { attr_span, .. } = tcx.codegen_fn_attrs(def_id).inline else { + return; + }; + + if let Err(reason) = + is_inline_valid_on_fn(tcx, def_id).and_then(|_| is_inline_valid_on_body(tcx, body)) + { + tcx.dcx().emit_err(crate::errors::InvalidForceInline { + attr_span, + callee_span: tcx.def_span(def_id), + callee: tcx.def_path_str(def_id), + reason, + }); + } +} + +pub fn is_inline_valid_on_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<(), &'static str> { + let codegen_attrs = tcx.codegen_fn_attrs(def_id); + if tcx.has_attr(def_id, sym::rustc_no_mir_inline) { + return Err("#[rustc_no_mir_inline]"); + } + + // FIXME(#127234): Coverage instrumentation currently doesn't handle inlined + // MIR correctly when Modified Condition/Decision Coverage is enabled. + if tcx.sess.instrument_coverage_mcdc() { + return Err("incompatible with MC/DC coverage"); + } + + let ty = tcx.type_of(def_id); + if match ty.instantiate_identity().kind() { + ty::FnDef(..) => tcx.fn_sig(def_id).instantiate_identity().c_variadic(), + ty::Closure(_, args) => args.as_closure().sig().c_variadic(), + _ => false, + } { + return Err("C variadic"); + } + + if codegen_attrs.flags.contains(CodegenFnAttrFlags::COLD) { + return Err("cold"); + } + + // Intrinsic fallback bodies are automatically made cross-crate inlineable, + // but at this stage we don't know whether codegen knows the intrinsic, + // so just conservatively don't inline it. This also ensures that we do not + // accidentally inline the body of an intrinsic that *must* be overridden. + if tcx.has_attr(def_id, sym::rustc_intrinsic) { + return Err("callee is an intrinsic"); + } + + Ok(()) +} + +pub fn is_inline_valid_on_body<'tcx>( + _: TyCtxt<'tcx>, + body: &Body<'tcx>, +) -> Result<(), &'static str> { + if body + .basic_blocks + .iter() + .any(|bb| matches!(bb.terminator().kind, TerminatorKind::TailCall { .. })) + { + return Err("can't inline functions with tail calls"); + } + + Ok(()) +} diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 790d56860d28..90c31a2caa32 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1107,3 +1107,15 @@ impl<'a> Subdiagnostic for Rust2024IncompatiblePatSugg<'a> { ); } } + +#[derive(Diagnostic)] +#[diag(mir_build_force_inline)] +#[note] +pub(crate) struct InvalidForceInline { + #[primary_span] + pub attr_span: Span, + #[label(mir_build_callee)] + pub callee_span: Span, + pub callee: String, + pub reason: &'static str, +} diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 467725841dcd..76a35355de72 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -15,6 +15,7 @@ // "Go to file" feature to silently ignore all files in the module, probably // because it assumes that "build" is a build-output directory. See #134365. mod builder; +pub mod check_inline; mod check_tail_calls; mod check_unsafety; mod errors; diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index ce1c635d1b9f..49db522cf0ee 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -1,7 +1,7 @@ use rustc_ast as ast; use rustc_hir::LangItem; use rustc_middle::bug; -use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::ty::{self, ScalarInt, TyCtxt, TypeVisitableExt as _}; use tracing::trace; @@ -10,11 +10,11 @@ use crate::builder::parse_float_into_scalar; pub(crate) fn lit_to_const<'tcx>( tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>, -) -> Result, LitToConstError> { +) -> ty::Const<'tcx> { let LitToConstInput { lit, ty, neg } = lit_input; if let Err(guar) = ty.error_reported() { - return Ok(ty::Const::new_error(tcx, guar)); + return ty::Const::new_error(tcx, guar); } let trunc = |n| { @@ -28,8 +28,8 @@ pub(crate) fn lit_to_const<'tcx>( let result = width.truncate(n); trace!("trunc result: {}", result); - Ok(ScalarInt::try_from_uint(result, width) - .unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result))) + ScalarInt::try_from_uint(result, width) + .unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result)) }; let valtree = match (lit, ty.kind()) { @@ -57,20 +57,20 @@ pub(crate) fn lit_to_const<'tcx>( } (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { let scalar_int = - trunc(if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() })?; + trunc(if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() }); ty::ValTree::from_scalar_int(scalar_int) } (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()), (ast::LitKind::Float(n, _), ty::Float(fty)) => { - let bits = parse_float_into_scalar(*n, *fty, neg).ok_or_else(|| { + let bits = parse_float_into_scalar(*n, *fty, neg).unwrap_or_else(|| { tcx.dcx().bug(format!("couldn't parse float literal: {:?}", lit_input.lit)) - })?; + }); ty::ValTree::from_scalar_int(bits) } (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()), - (ast::LitKind::Err(guar), _) => return Err(LitToConstError::Reported(*guar)), - _ => return Err(LitToConstError::TypeError), + (ast::LitKind::Err(guar), _) => return ty::Const::new_error(tcx, *guar), + _ => return ty::Const::new_misc_error(tcx), }; - Ok(ty::Const::new_value(tcx, valtree, ty)) + ty::Const::new_value(tcx, valtree, ty) } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 2b3c98db966c..3853b95f78b4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,3 +1,5 @@ +use core::ops::ControlFlow; + use rustc_abi::{FieldIdx, VariantIdx}; use rustc_apfloat::Float; use rustc_data_structures::fx::FxHashSet; @@ -8,7 +10,9 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::thir::{FieldPat, Pat, PatKind}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeVisitor, ValTree}; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, ValTree, +}; use rustc_middle::{mir, span_bug}; use rustc_span::def_id::DefId; use rustc_span::{Span, sym}; @@ -185,7 +189,7 @@ impl<'tcx> ConstToPat<'tcx> { if !inlined_const_as_pat.references_error() { // Always check for `PartialEq` if we had no other errors yet. - if !type_has_partial_eq_impl(self.tcx, typing_env, ty).0 { + if !type_has_partial_eq_impl(self.tcx, typing_env, ty).has_impl { let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty }); extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err); return self.mk_err(err, ty); @@ -219,12 +223,13 @@ impl<'tcx> ConstToPat<'tcx> { // Extremely important check for all ADTs! Make sure they opted-in to be used in // patterns. debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty); - let (_impls_partial_eq, derived, structural, impl_def_id) = - type_has_partial_eq_impl(self.tcx, self.typing_env, ty); + let PartialEqImplStatus { + is_derived, structural_partial_eq, non_blanket_impl, .. + } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty); let (manual_partialeq_impl_span, manual_partialeq_impl_note) = - match (structural, impl_def_id) { + match (structural_partial_eq, non_blanket_impl) { (true, _) => (None, false), - (_, Some(def_id)) if def_id.is_local() && !derived => { + (_, Some(def_id)) if def_id.is_local() && !is_derived => { (Some(tcx.def_span(def_id)), false) } _ => (None, true), @@ -379,41 +384,50 @@ fn extend_type_not_partial_eq<'tcx>( adts_without_partialeq: FxHashSet, /// The user has written `impl PartialEq for Ty` which means it's non-structual, /// but we don't have a span to point at, so we'll just add them as a `note`. - manual: Vec>, + manual: FxHashSet>, /// The type has no `PartialEq` implementation, neither manual or derived, but /// we don't have a span to point at, so we'll just add them as a `note`. - without: Vec>, + without: FxHashSet>, } impl<'tcx> TypeVisitor> for UsedParamsNeedInstantiationVisitor<'tcx> { + type Result = ControlFlow<()>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if let ty::Adt(def, _args) = ty.kind() { - let ty_def_id = def.did(); - let ty_def_span = self.tcx.def_span(ty_def_id); - let (impls_partial_eq, derived, structural, impl_def_id) = - type_has_partial_eq_impl(self.tcx, self.typing_env, ty); - match (impls_partial_eq, derived, structural, impl_def_id) { - (_, _, true, _) => {} - (true, false, _, Some(def_id)) if def_id.is_local() => { - self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id)); - } - (true, false, _, _) if ty_def_id.is_local() => { - self.adts_with_manual_partialeq.insert(ty_def_span); - } - (false, _, _, _) if ty_def_id.is_local() => { - self.adts_without_partialeq.insert(ty_def_span); - } - (true, false, _, _) => { - self.manual.push(ty); - } - (false, _, _, _) => { - self.without.push(ty); - } - _ => {} - }; + match ty.kind() { + ty::Dynamic(..) => return ControlFlow::Break(()), + ty::FnPtr(..) => return ControlFlow::Continue(()), + ty::Adt(def, _args) => { + let ty_def_id = def.did(); + let ty_def_span = self.tcx.def_span(ty_def_id); + let PartialEqImplStatus { + has_impl, + is_derived, + structural_partial_eq, + non_blanket_impl, + } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty); + match (has_impl, is_derived, structural_partial_eq, non_blanket_impl) { + (_, _, true, _) => {} + (true, false, _, Some(def_id)) if def_id.is_local() => { + self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id)); + } + (true, false, _, _) if ty_def_id.is_local() => { + self.adts_with_manual_partialeq.insert(ty_def_span); + } + (false, _, _, _) if ty_def_id.is_local() => { + self.adts_without_partialeq.insert(ty_def_span); + } + (true, false, _, _) => { + self.manual.insert(ty); + } + (false, _, _, _) => { + self.without.insert(ty); + } + _ => {} + }; + ty.super_visit_with(self) + } + _ => ty.super_visit_with(self), } - use rustc_middle::ty::TypeSuperVisitable; - ty.super_visit_with(self) } } let mut v = UsedParamsNeedInstantiationVisitor { @@ -421,10 +435,12 @@ fn extend_type_not_partial_eq<'tcx>( typing_env, adts_with_manual_partialeq: FxHashSet::default(), adts_without_partialeq: FxHashSet::default(), - manual: vec![], - without: vec![], + manual: FxHashSet::default(), + without: FxHashSet::default(), }; - v.visit_ty(ty); + if v.visit_ty(ty).is_break() { + return; + } #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering for span in v.adts_with_manual_partialeq { err.span_note(span, "the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details"); @@ -436,29 +452,38 @@ fn extend_type_not_partial_eq<'tcx>( "must be annotated with `#[derive(PartialEq)]` to be usable in patterns", ); } - for ty in v.manual { + #[allow(rustc::potential_query_instability)] + let mut manual: Vec<_> = v.manual.into_iter().map(|t| t.to_string()).collect(); + manual.sort(); + for ty in manual { err.note(format!( "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details" )); } - for ty in v.without { + #[allow(rustc::potential_query_instability)] + let mut without: Vec<_> = v.without.into_iter().map(|t| t.to_string()).collect(); + without.sort(); + for ty in without { err.note(format!( "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns" )); } } +#[derive(Debug)] +struct PartialEqImplStatus { + has_impl: bool, + is_derived: bool, + structural_partial_eq: bool, + non_blanket_impl: Option, +} + #[instrument(level = "trace", skip(tcx), ret)] fn type_has_partial_eq_impl<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, -) -> ( - /* has impl */ bool, - /* is derived */ bool, - /* structural partial eq */ bool, - /* non-blanket impl */ Option, -) { +) -> PartialEqImplStatus { let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); // double-check there even *is* a semantic `PartialEq` to dispatch to. // @@ -495,10 +520,10 @@ fn type_has_partial_eq_impl<'tcx>( // that patterns can only do things that the code could also do without patterns, but it is // needed for backwards compatibility. The actual pattern matching compares primitive values, // `PartialEq::eq` never gets invoked, so there's no risk of us running non-const code. - ( - infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation), - automatically_derived, - structural_peq, - impl_def_id, - ) + PartialEqImplStatus { + has_impl: infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation), + is_derived: automatically_derived, + structural_partial_eq: structural_peq, + non_blanket_impl: impl_def_id, + } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index bdf243c87b6f..44b038bb5faf 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -13,7 +13,7 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd}; use rustc_index::Idx; use rustc_lint as lint; -use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::thir::{ Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, }; @@ -154,7 +154,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pattern_range_endpoint( &mut self, - expr: Option<&'tcx hir::Expr<'tcx>>, + expr: Option<&'tcx hir::PatExpr<'tcx>>, ) -> Result< (Option>, Option>, Option), ErrorGuaranteed, @@ -200,13 +200,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// This is only called when the range is already known to be malformed. fn error_on_literal_overflow( &self, - expr: Option<&'tcx hir::Expr<'tcx>>, + expr: Option<&'tcx hir::PatExpr<'tcx>>, ty: Ty<'tcx>, ) -> Result<(), ErrorGuaranteed> { - use hir::{ExprKind, UnOp}; use rustc_ast::ast::LitKind; - let Some(mut expr) = expr else { + let Some(expr) = expr else { return Ok(()); }; let span = expr.span; @@ -214,12 +213,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // We need to inspect the original expression, because if we only inspect the output of // `eval_bits`, an overflowed value has already been wrapped around. // We mostly copy the logic from the `rustc_lint::OVERFLOWING_LITERALS` lint. - let mut negated = false; - if let ExprKind::Unary(UnOp::Neg, sub_expr) = expr.kind { - negated = true; - expr = sub_expr; - } - let ExprKind::Lit(lit) = expr.kind else { + let hir::PatExprKind::Lit { lit, negated } = expr.kind else { return Ok(()); }; let LitKind::Int(lit_val, _) = lit.node else { @@ -248,8 +242,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pattern_range( &mut self, - lo_expr: Option<&'tcx hir::Expr<'tcx>>, - hi_expr: Option<&'tcx hir::Expr<'tcx>>, + lo_expr: Option<&'tcx hir::PatExpr<'tcx>>, + hi_expr: Option<&'tcx hir::PatExpr<'tcx>>, end: RangeEnd, ty: Ty<'tcx>, span: Span, @@ -330,7 +324,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir::PatKind::Never => PatKind::Never, - hir::PatKind::Lit(value) => self.lower_lit(value), + hir::PatKind::Expr(value) => self.lower_lit(value), hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => { let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref()); @@ -435,6 +429,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), }; @@ -659,31 +656,21 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// The special case for negation exists to allow things like `-128_i8` /// which would overflow if we tried to evaluate `128_i8` and then negate /// afterwards. - fn lower_lit(&mut self, expr: &'tcx hir::Expr<'tcx>) -> PatKind<'tcx> { - let (lit, neg) = match expr.kind { - hir::ExprKind::Path(ref qpath) => { + fn lower_lit(&mut self, expr: &'tcx hir::PatExpr<'tcx>) -> PatKind<'tcx> { + let (lit, neg) = match &expr.kind { + hir::PatExprKind::Path(qpath) => { return self.lower_path(qpath, expr.hir_id, expr.span).kind; } - hir::ExprKind::ConstBlock(ref anon_const) => { + hir::PatExprKind::ConstBlock(anon_const) => { return self.lower_inline_const(anon_const, expr.hir_id, expr.span); } - hir::ExprKind::Lit(ref lit) => (lit, false), - hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => { - let hir::ExprKind::Lit(ref lit) = expr.kind else { - span_bug!(expr.span, "not a literal: {:?}", expr); - }; - (lit, true) - } - _ => span_bug!(expr.span, "not a literal: {:?}", expr), + hir::PatExprKind::Lit { lit, negated } => (lit, *negated), }; - let ct_ty = self.typeck_results.expr_ty(expr); + let ct_ty = self.typeck_results.node_type(expr.hir_id); let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg }; - match self.tcx.at(expr.span).lit_to_const(lit_input) { - Ok(constant) => self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind, - Err(LitToConstError::Reported(e)) => PatKind::Error(e), - Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), - } + let constant = self.tcx.at(expr.span).lit_to_const(lit_input); + self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind } } diff --git a/compiler/rustc_mir_dataflow/src/debuginfo.rs b/compiler/rustc_mir_dataflow/src/debuginfo.rs index fd5e8cf29555..0d25ce91c9a9 100644 --- a/compiler/rustc_mir_dataflow/src/debuginfo.rs +++ b/compiler/rustc_mir_dataflow/src/debuginfo.rs @@ -1,17 +1,17 @@ -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; /// Return the set of locals that appear in debuginfo. -pub fn debuginfo_locals(body: &Body<'_>) -> BitSet { - let mut visitor = DebuginfoLocals(BitSet::new_empty(body.local_decls.len())); +pub fn debuginfo_locals(body: &Body<'_>) -> DenseBitSet { + let mut visitor = DebuginfoLocals(DenseBitSet::new_empty(body.local_decls.len())); for debuginfo in body.var_debug_info.iter() { visitor.visit_var_debug_info(debuginfo); } visitor.0 } -struct DebuginfoLocals(BitSet); +struct DebuginfoLocals(DenseBitSet); impl Visitor<'_> for DebuginfoLocals { fn visit_local(&mut self, local: Local, _: PlaceContext, _: Location) { diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index 89ff93d9943a..c46ae9775cf6 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -4,7 +4,7 @@ use std::cmp::Ordering; use std::ops::{Deref, DerefMut}; #[cfg(debug_assertions)] -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{self, BasicBlock, Location}; use super::{Analysis, Direction, Effect, EffectIndex, Results}; @@ -71,7 +71,7 @@ where state_needs_reset: bool, #[cfg(debug_assertions)] - reachable_blocks: BitSet, + reachable_blocks: DenseBitSet, } impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A> diff --git a/compiler/rustc_mir_dataflow/src/framework/fmt.rs b/compiler/rustc_mir_dataflow/src/framework/fmt.rs index faf2c411ddef..38599cd09493 100644 --- a/compiler/rustc_mir_dataflow/src/framework/fmt.rs +++ b/compiler/rustc_mir_dataflow/src/framework/fmt.rs @@ -4,7 +4,7 @@ use std::fmt; use rustc_index::Idx; -use rustc_index::bit_set::{BitSet, ChunkedBitSet, MixedBitSet}; +use rustc_index::bit_set::{ChunkedBitSet, DenseBitSet, MixedBitSet}; use super::lattice::MaybeReachable; @@ -73,7 +73,7 @@ where // Impls -impl DebugWithContext for BitSet +impl DebugWithContext for DenseBitSet where T: Idx + DebugWithContext, { diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 60c97710c7fe..e457b514936b 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -9,7 +9,7 @@ use std::{io, ops, str}; use regex::Regex; use rustc_hir::def_id::DefId; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{ self, BasicBlock, Body, Location, create_dump_file, dump_enabled, graphviz_safe_def_name, traversal, @@ -205,7 +205,7 @@ where // the operations that involve the mutation, i.e. within the `borrow_mut`. cursor: RefCell>, style: OutputStyle, - reachable: BitSet, + reachable: DenseBitSet, } impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A> diff --git a/compiler/rustc_mir_dataflow/src/framework/lattice.rs b/compiler/rustc_mir_dataflow/src/framework/lattice.rs index cb8159ce37bf..919e346bda74 100644 --- a/compiler/rustc_mir_dataflow/src/framework/lattice.rs +++ b/compiler/rustc_mir_dataflow/src/framework/lattice.rs @@ -39,7 +39,7 @@ //! [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set use rustc_index::Idx; -use rustc_index::bit_set::{BitSet, MixedBitSet}; +use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use crate::framework::BitSetExt; @@ -68,10 +68,10 @@ pub trait HasTop { const TOP: Self; } -/// A `BitSet` represents the lattice formed by the powerset of all possible values of -/// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices, -/// one for each possible value of `T`. -impl JoinSemiLattice for BitSet { +/// A `DenseBitSet` represents the lattice formed by the powerset of all possible values of the +/// index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices, one +/// for each possible value of `T`. +impl JoinSemiLattice for DenseBitSet { fn join(&mut self, other: &Self) -> bool { self.union(other) } diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 3de2c6e3f47c..60c5cb0cae8a 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -35,7 +35,7 @@ use std::cmp::Ordering; use rustc_data_structures::work_queue::WorkQueue; -use rustc_index::bit_set::{BitSet, MixedBitSet}; +use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use rustc_index::{Idx, IndexVec}; use rustc_middle::bug; use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges, traversal}; @@ -65,7 +65,7 @@ pub trait BitSetExt { fn contains(&self, elem: T) -> bool; } -impl BitSetExt for BitSet { +impl BitSetExt for DenseBitSet { fn contains(&self, elem: T) -> bool { self.contains(elem) } @@ -334,7 +334,7 @@ pub trait GenKill { } } -impl GenKill for BitSet { +impl GenKill for DenseBitSet { fn gen_(&mut self, elem: T) { self.insert(elem); } diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 8e7d4ab0fa33..5b3a9ccba695 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -84,13 +84,13 @@ impl MockAnalysis<'_, D> { /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to /// avoid colliding with the statement/terminator effects. - fn mock_entry_set(&self, bb: BasicBlock) -> BitSet { + fn mock_entry_set(&self, bb: BasicBlock) -> DenseBitSet { let mut ret = self.bottom_value(self.body); ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index()); ret } - fn mock_entry_states(&self) -> IndexVec> { + fn mock_entry_states(&self) -> IndexVec> { let empty = self.bottom_value(self.body); let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks); @@ -121,7 +121,7 @@ impl MockAnalysis<'_, D> { /// For example, the expected state when calling /// `seek_before_primary_effect(Location { block: 2, statement_index: 2 })` /// would be `[102, 0, 1, 2, 3, 4]`. - fn expected_state_at_target(&self, target: SeekTarget) -> BitSet { + fn expected_state_at_target(&self, target: SeekTarget) -> DenseBitSet { let block = target.block(); let mut ret = self.bottom_value(self.body); ret.insert(Self::BASIC_BLOCK_OFFSET + block.index()); @@ -155,13 +155,13 @@ impl MockAnalysis<'_, D> { } impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { - type Domain = BitSet; + type Domain = DenseBitSet; type Direction = D; const NAME: &'static str = "mock"; fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - BitSet::new_empty(Self::BASIC_BLOCK_OFFSET + body.basic_blocks.len()) + DenseBitSet::new_empty(Self::BASIC_BLOCK_OFFSET + body.basic_blocks.len()) } fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 217594b3238a..f2ef5018c495 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -1,4 +1,4 @@ -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; @@ -21,12 +21,12 @@ impl MaybeBorrowedLocals { } impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals { - type Domain = BitSet; + type Domain = DenseBitSet; const NAME: &'static str = "maybe_borrowed_locals"; fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { // bottom = unborrowed - BitSet::new_empty(body.local_decls().len()) + DenseBitSet::new_empty(body.local_decls().len()) } fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) { @@ -137,8 +137,8 @@ where } /// The set of locals that are borrowed at some point in the MIR body. -pub fn borrowed_locals(body: &Body<'_>) -> BitSet { - struct Borrowed(BitSet); +pub fn borrowed_locals(body: &Body<'_>) -> DenseBitSet { + struct Borrowed(DenseBitSet); impl GenKill for Borrowed { #[inline] @@ -151,7 +151,7 @@ pub fn borrowed_locals(body: &Body<'_>) -> BitSet { } } - let mut borrowed = Borrowed(BitSet::new_empty(body.local_decls.len())); + let mut borrowed = Borrowed(DenseBitSet::new_empty(body.local_decls.len())); TransferFunction { trans: &mut borrowed }.visit_body(body); borrowed.0 } diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 769f9c7cfc3e..760f94af52dd 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -2,7 +2,7 @@ use std::assert_matches::assert_matches; use rustc_abi::VariantIdx; use rustc_index::Idx; -use rustc_index::bit_set::{BitSet, MixedBitSet}; +use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use rustc_middle::bug; use rustc_middle::mir::{self, Body, CallReturnPlaces, Location, TerminatorEdges}; use rustc_middle::ty::util::Discr; @@ -207,7 +207,7 @@ pub struct MaybeUninitializedPlaces<'a, 'tcx> { move_data: &'a MoveData<'tcx>, mark_inactive_variants_as_uninit: bool, - skip_unreachable_unwind: BitSet, + skip_unreachable_unwind: DenseBitSet, } impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { @@ -217,7 +217,7 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { body, move_data, mark_inactive_variants_as_uninit: false, - skip_unreachable_unwind: BitSet::new_empty(body.basic_blocks.len()), + skip_unreachable_unwind: DenseBitSet::new_empty(body.basic_blocks.len()), } } @@ -233,7 +233,7 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { pub fn skipping_unreachable_unwind( mut self, - unreachable_unwind: BitSet, + unreachable_unwind: DenseBitSet, ) -> Self { self.skip_unreachable_unwind = unreachable_unwind; self diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index b2050a6adf98..6ec1b03a34e6 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -1,4 +1,4 @@ -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{ self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges, @@ -26,14 +26,14 @@ use crate::{Analysis, Backward, GenKill}; pub struct MaybeLiveLocals; impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { - type Domain = BitSet; + type Domain = DenseBitSet; type Direction = Backward; const NAME: &'static str = "liveness"; fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { // bottom = not live - BitSet::new_empty(body.local_decls.len()) + DenseBitSet::new_empty(body.local_decls.len()) } fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { @@ -81,7 +81,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { } } -pub struct TransferFunction<'a>(pub &'a mut BitSet); +pub struct TransferFunction<'a>(pub &'a mut DenseBitSet); impl<'tcx> Visitor<'tcx> for TransferFunction<'_> { fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) { @@ -117,7 +117,7 @@ impl<'tcx> Visitor<'tcx> for TransferFunction<'_> { } } -struct YieldResumeEffect<'a>(&'a mut BitSet); +struct YieldResumeEffect<'a>(&'a mut DenseBitSet); impl<'tcx> Visitor<'tcx> for YieldResumeEffect<'_> { fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) { @@ -137,7 +137,7 @@ enum DefUse { } impl DefUse { - fn apply(state: &mut BitSet, place: Place<'_>, context: PlaceContext) { + fn apply(state: &mut DenseBitSet, place: Place<'_>, context: PlaceContext) { match DefUse::for_place(place, context) { Some(DefUse::Def) => state.kill(place.local), Some(DefUse::Use) => state.gen_(place.local), @@ -204,7 +204,7 @@ impl DefUse { /// /// All of the caveats of `MaybeLiveLocals` apply. pub struct MaybeTransitiveLiveLocals<'a> { - always_live: &'a BitSet, + always_live: &'a DenseBitSet, } impl<'a> MaybeTransitiveLiveLocals<'a> { @@ -212,20 +212,20 @@ impl<'a> MaybeTransitiveLiveLocals<'a> { /// considered live. /// /// This should include at least all locals that are ever borrowed. - pub fn new(always_live: &'a BitSet) -> Self { + pub fn new(always_live: &'a DenseBitSet) -> Self { MaybeTransitiveLiveLocals { always_live } } } impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { - type Domain = BitSet; + type Domain = DenseBitSet; type Direction = Backward; const NAME: &'static str = "transitive liveness"; fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { // bottom = not live - BitSet::new_empty(body.local_decls.len()) + DenseBitSet::new_empty(body.local_decls.len()) } fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 65b480d3a5ef..e3aa8f5a6201 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -10,8 +10,8 @@ use crate::{Analysis, GenKill, ResultsCursor}; /// The set of locals in a MIR body that do not have `StorageLive`/`StorageDead` annotations. /// /// These locals have fixed storage for the duration of the body. -pub fn always_storage_live_locals(body: &Body<'_>) -> BitSet { - let mut always_live_locals = BitSet::new_filled(body.local_decls.len()); +pub fn always_storage_live_locals(body: &Body<'_>) -> DenseBitSet { + let mut always_live_locals = DenseBitSet::new_filled(body.local_decls.len()); for block in &*body.basic_blocks { for statement in &block.statements { @@ -25,23 +25,23 @@ pub fn always_storage_live_locals(body: &Body<'_>) -> BitSet { } pub struct MaybeStorageLive<'a> { - always_live_locals: Cow<'a, BitSet>, + always_live_locals: Cow<'a, DenseBitSet>, } impl<'a> MaybeStorageLive<'a> { - pub fn new(always_live_locals: Cow<'a, BitSet>) -> Self { + pub fn new(always_live_locals: Cow<'a, DenseBitSet>) -> Self { MaybeStorageLive { always_live_locals } } } impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageLive<'a> { - type Domain = BitSet; + type Domain = DenseBitSet; const NAME: &'static str = "maybe_storage_live"; fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { // bottom = dead - BitSet::new_empty(body.local_decls.len()) + DenseBitSet::new_empty(body.local_decls.len()) } fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { @@ -67,23 +67,23 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageLive<'a> { } pub struct MaybeStorageDead<'a> { - always_live_locals: Cow<'a, BitSet>, + always_live_locals: Cow<'a, DenseBitSet>, } impl<'a> MaybeStorageDead<'a> { - pub fn new(always_live_locals: Cow<'a, BitSet>) -> Self { + pub fn new(always_live_locals: Cow<'a, DenseBitSet>) -> Self { MaybeStorageDead { always_live_locals } } } impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageDead<'a> { - type Domain = BitSet; + type Domain = DenseBitSet; const NAME: &'static str = "maybe_storage_dead"; fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { // bottom = live - BitSet::new_empty(body.local_decls.len()) + DenseBitSet::new_empty(body.local_decls.len()) } fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { @@ -125,13 +125,13 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { } impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { - type Domain = BitSet; + type Domain = DenseBitSet; const NAME: &'static str = "requires_storage"; fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { // bottom = dead - BitSet::new_empty(body.local_decls.len()) + DenseBitSet::new_empty(body.local_decls.len()) } fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { @@ -304,7 +304,7 @@ impl<'tcx> MaybeRequiresStorage<'_, 'tcx> { struct MoveVisitor<'a, 'mir, 'tcx> { borrowed_locals: &'a mut BorrowedLocalsResults<'mir, 'tcx>, - state: &'a mut BitSet, + state: &'a mut DenseBitSet, } impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> { diff --git a/compiler/rustc_mir_dataflow/src/points.rs b/compiler/rustc_mir_dataflow/src/points.rs index 74209da876ab..5d2a78acbf52 100644 --- a/compiler/rustc_mir_dataflow/src/points.rs +++ b/compiler/rustc_mir_dataflow/src/points.rs @@ -1,4 +1,4 @@ -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::SparseIntervalMatrix; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::{self, BasicBlock, Body, Location}; @@ -102,7 +102,7 @@ pub fn save_as_intervals<'tcx, N, A>( ) -> SparseIntervalMatrix where N: Idx, - A: Analysis<'tcx, Domain = BitSet>, + A: Analysis<'tcx, Domain = DenseBitSet>, { let values = SparseIntervalMatrix::new(elements.num_points()); let mut visitor = Visitor { elements, values }; @@ -122,7 +122,7 @@ struct Visitor<'a, N: Idx> { impl<'mir, 'tcx, A, N> ResultsVisitor<'mir, 'tcx, A> for Visitor<'_, N> where - A: Analysis<'tcx, Domain = BitSet>, + A: Analysis<'tcx, Domain = DenseBitSet>, N: Idx, { fn visit_after_primary_statement_effect( diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 9328870c7ae0..a51af8c40fd4 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -6,7 +6,7 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxIndexSet, StdEntry}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -399,7 +399,7 @@ impl<'tcx> Map<'tcx> { &mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - exclude: BitSet, + exclude: DenseBitSet, value_limit: Option, ) { // Start by constructing the places for each bare local. @@ -912,9 +912,9 @@ pub fn iter_fields<'tcx>( } /// Returns all locals with projections that have their reference or address taken. -pub fn excluded_locals(body: &Body<'_>) -> BitSet { +pub fn excluded_locals(body: &Body<'_>) -> DenseBitSet { struct Collector { - result: BitSet, + result: DenseBitSet, } impl<'tcx> Visitor<'tcx> for Collector { @@ -932,7 +932,7 @@ pub fn excluded_locals(body: &Body<'_>) -> BitSet { } } - let mut collector = Collector { result: BitSet::new_empty(body.local_decls.len()) }; + let mut collector = Collector { result: DenseBitSet::new_empty(body.local_decls.len()) }; collector.visit_body(body); collector.result } diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index d00bfc66a6a5..b0c023cca824 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -19,6 +19,17 @@ mir_transform_ffi_unwind_call = call to {$foreign -> mir_transform_fn_item_ref = taking a reference to a function item does not give a function pointer .suggestion = cast `{$ident}` to obtain a function pointer +mir_transform_force_inline = + `{$callee}` could not be inlined into `{$caller}` but is required to be inlined + .call = ...`{$callee}` called here + .attr = inlining due to this annotation + .caller = within `{$caller}`... + .callee = `{$callee}` defined here + .note = could not be inlined due to: {$reason} + +mir_transform_force_inline_justification = + `{$callee}` is required to be inlined to: {$sym} + mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspend point, but should not be .label = the value is held across this suspend point .note = {$reason} diff --git a/compiler/rustc_mir_transform/src/copy_prop.rs b/compiler/rustc_mir_transform/src/copy_prop.rs index 9b3443d3209e..f149bf97cde6 100644 --- a/compiler/rustc_mir_transform/src/copy_prop.rs +++ b/compiler/rustc_mir_transform/src/copy_prop.rs @@ -1,5 +1,5 @@ use rustc_index::IndexSlice; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -34,7 +34,7 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp { let fully_moved = fully_moved_locals(&ssa, body); debug!(?fully_moved); - let mut storage_to_remove = BitSet::new_empty(fully_moved.domain_size()); + let mut storage_to_remove = DenseBitSet::new_empty(fully_moved.domain_size()); for (local, &head) in ssa.copy_classes().iter_enumerated() { if local != head { storage_to_remove.insert(head); @@ -68,8 +68,8 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp { /// This means that replacing it by a copy of `_a` if ok, since this copy happens before `_c` is /// moved, and therefore that `_d` is moved. #[instrument(level = "trace", skip(ssa, body))] -fn fully_moved_locals(ssa: &SsaLocals, body: &Body<'_>) -> BitSet { - let mut fully_moved = BitSet::new_filled(body.local_decls.len()); +fn fully_moved_locals(ssa: &SsaLocals, body: &Body<'_>) -> DenseBitSet { + let mut fully_moved = DenseBitSet::new_filled(body.local_decls.len()); for (_, rvalue, _) in ssa.assignments(body) { let (Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) @@ -96,9 +96,9 @@ fn fully_moved_locals(ssa: &SsaLocals, body: &Body<'_>) -> BitSet { /// Utility to help performing substitution of `*pattern` by `target`. struct Replacer<'a, 'tcx> { tcx: TyCtxt<'tcx>, - fully_moved: BitSet, - storage_to_remove: BitSet, - borrowed_locals: &'a BitSet, + fully_moved: DenseBitSet, + storage_to_remove: DenseBitSet, + borrowed_locals: &'a DenseBitSet, copy_classes: &'a IndexSlice, } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index f6536d78761a..a3715b5d4855 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -60,7 +60,7 @@ use rustc_errors::pluralize; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_hir::{CoroutineDesugaring, CoroutineKind}; -use rustc_index::bit_set::{BitMatrix, BitSet, GrowableBitSet}; +use rustc_index::bit_set::{BitMatrix, DenseBitSet, GrowableBitSet}; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -185,13 +185,13 @@ struct TransformVisitor<'tcx> { remap: IndexVec, VariantIdx, FieldIdx)>>, // A map from a suspension point in a block to the locals which have live storage at that point - storage_liveness: IndexVec>>, + storage_liveness: IndexVec>>, // A list of suspension points, generated during the transform suspension_points: Vec>, // The set of locals that have no `StorageLive`/`StorageDead` annotations. - always_live_locals: BitSet, + always_live_locals: DenseBitSet, // The original RETURN_PLACE local old_ret_local: Local, @@ -633,7 +633,7 @@ struct LivenessInfo { saved_locals: CoroutineSavedLocals, /// The set of saved locals live at each suspension point. - live_locals_at_suspension_points: Vec>, + live_locals_at_suspension_points: Vec>, /// Parallel vec to the above with SourceInfo for each yield terminator. source_info_at_suspension_points: Vec, @@ -645,7 +645,7 @@ struct LivenessInfo { /// For every suspending block, the locals which are storage-live across /// that suspension point. - storage_liveness: IndexVec>>, + storage_liveness: IndexVec>>, } /// Computes which locals have to be stored in the state-machine for the @@ -659,7 +659,7 @@ struct LivenessInfo { fn locals_live_across_suspend_points<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - always_live_locals: &BitSet, + always_live_locals: &DenseBitSet, movable: bool, ) -> LivenessInfo { // Calculate when MIR locals have live storage. This gives us an upper bound of their @@ -688,7 +688,7 @@ fn locals_live_across_suspend_points<'tcx>( let mut storage_liveness_map = IndexVec::from_elem(None, &body.basic_blocks); let mut live_locals_at_suspension_points = Vec::new(); let mut source_info_at_suspension_points = Vec::new(); - let mut live_locals_at_any_suspension_point = BitSet::new_empty(body.local_decls.len()); + let mut live_locals_at_any_suspension_point = DenseBitSet::new_empty(body.local_decls.len()); for (block, data) in body.basic_blocks.iter_enumerated() { if let TerminatorKind::Yield { .. } = data.terminator().kind { @@ -768,7 +768,7 @@ fn locals_live_across_suspend_points<'tcx>( /// `CoroutineSavedLocal` is indexed in terms of the elements in this set; /// i.e. `CoroutineSavedLocal::new(1)` corresponds to the second local /// included in this set. -struct CoroutineSavedLocals(BitSet); +struct CoroutineSavedLocals(DenseBitSet); impl CoroutineSavedLocals { /// Returns an iterator over each `CoroutineSavedLocal` along with the `Local` it corresponds @@ -777,11 +777,11 @@ impl CoroutineSavedLocals { self.iter().enumerate().map(|(i, l)| (CoroutineSavedLocal::from(i), l)) } - /// Transforms a `BitSet` that contains only locals saved across yield points to the - /// equivalent `BitSet`. - fn renumber_bitset(&self, input: &BitSet) -> BitSet { + /// Transforms a `DenseBitSet` that contains only locals saved across yield points to the + /// equivalent `DenseBitSet`. + fn renumber_bitset(&self, input: &DenseBitSet) -> DenseBitSet { assert!(self.superset(input), "{:?} not a superset of {:?}", self.0, input); - let mut out = BitSet::new_empty(self.count()); + let mut out = DenseBitSet::new_empty(self.count()); for (saved_local, local) in self.iter_enumerated() { if input.contains(local) { out.insert(saved_local); @@ -801,7 +801,7 @@ impl CoroutineSavedLocals { } impl ops::Deref for CoroutineSavedLocals { - type Target = BitSet; + type Target = DenseBitSet; fn deref(&self) -> &Self::Target { &self.0 @@ -815,7 +815,7 @@ impl ops::Deref for CoroutineSavedLocals { fn compute_storage_conflicts<'mir, 'tcx>( body: &'mir Body<'tcx>, saved_locals: &'mir CoroutineSavedLocals, - always_live_locals: BitSet, + always_live_locals: DenseBitSet, mut requires_storage: Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>, ) -> BitMatrix { assert_eq!(body.local_decls.len(), saved_locals.domain_size()); @@ -833,7 +833,7 @@ fn compute_storage_conflicts<'mir, 'tcx>( body, saved_locals, local_conflicts: BitMatrix::from_row_n(&ineligible_locals, body.local_decls.len()), - eligible_storage_live: BitSet::new_empty(body.local_decls.len()), + eligible_storage_live: DenseBitSet::new_empty(body.local_decls.len()), }; requires_storage.visit_reachable_with(body, &mut visitor); @@ -871,7 +871,7 @@ struct StorageConflictVisitor<'a, 'tcx> { // benchmarks for coroutines. local_conflicts: BitMatrix, // We keep this bitset as a buffer to avoid reallocating memory. - eligible_storage_live: BitSet, + eligible_storage_live: DenseBitSet, } impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> @@ -880,7 +880,7 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> fn visit_after_early_statement_effect( &mut self, _results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>, - state: &BitSet, + state: &DenseBitSet, _statement: &'a Statement<'tcx>, loc: Location, ) { @@ -890,7 +890,7 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> fn visit_after_early_terminator_effect( &mut self, _results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>, - state: &BitSet, + state: &DenseBitSet, _terminator: &'a Terminator<'tcx>, loc: Location, ) { @@ -899,7 +899,7 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> } impl StorageConflictVisitor<'_, '_> { - fn apply_state(&mut self, state: &BitSet, loc: Location) { + fn apply_state(&mut self, state: &DenseBitSet, loc: Location) { // Ignore unreachable blocks. if let TerminatorKind::Unreachable = self.body.basic_blocks[loc.block].terminator().kind { return; @@ -924,7 +924,7 @@ fn compute_layout<'tcx>( ) -> ( IndexVec, VariantIdx, FieldIdx)>>, CoroutineLayout<'tcx>, - IndexVec>>, + IndexVec>>, ) { let LivenessInfo { saved_locals, diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 9e80f1f1c4ac..a9111a5ef9b4 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -5,7 +5,7 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph::DirectedGraph; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op}; use tracing::{debug, debug_span, instrument}; @@ -77,7 +77,7 @@ impl CoverageCounters { /// counters or counter expressions for nodes and edges as required. pub(super) fn make_bcb_counters( graph: &CoverageGraph, - bcb_needs_counter: &BitSet, + bcb_needs_counter: &DenseBitSet, ) -> Self { let mut builder = CountersBuilder::new(graph, bcb_needs_counter); builder.make_bcb_counters(); @@ -220,13 +220,16 @@ fn sibling_out_edge_targets( /// the set of nodes that need counters. struct CountersBuilder<'a> { graph: &'a CoverageGraph, - bcb_needs_counter: &'a BitSet, + bcb_needs_counter: &'a DenseBitSet, site_counters: FxHashMap, } impl<'a> CountersBuilder<'a> { - fn new(graph: &'a CoverageGraph, bcb_needs_counter: &'a BitSet) -> Self { + fn new( + graph: &'a CoverageGraph, + bcb_needs_counter: &'a DenseBitSet, + ) -> Self { assert_eq!(graph.num_nodes(), bcb_needs_counter.domain_size()); Self { graph, bcb_needs_counter, site_counters: FxHashMap::default() } } diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index ad6774fccd6f..3fa8b063fa75 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::dominators::Dominators; use rustc_data_structures::graph::{self, DirectedGraph, StartNode}; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{self, BasicBlock, Terminator, TerminatorKind}; use tracing::debug; @@ -27,7 +27,7 @@ pub(crate) struct CoverageGraph { /// their relative order is consistent but arbitrary. dominator_order_rank: IndexVec, /// A loop header is a node that dominates one or more of its predecessors. - is_loop_header: BitSet, + is_loop_header: DenseBitSet, /// For each node, the loop header node of its nearest enclosing loop. /// This forms a linked list that can be traversed to find all enclosing loops. enclosing_loop_header: IndexVec>, @@ -72,7 +72,7 @@ impl CoverageGraph { predecessors, dominators: None, dominator_order_rank: IndexVec::from_elem_n(0, num_nodes), - is_loop_header: BitSet::new_empty(num_nodes), + is_loop_header: DenseBitSet::new_empty(num_nodes), enclosing_loop_header: IndexVec::from_elem_n(None, num_nodes), }; assert_eq!(num_nodes, this.num_nodes()); diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index 4185b3f4d4d8..8d0d92dc3677 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -3,7 +3,7 @@ use std::collections::BTreeSet; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph::DirectedGraph; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::coverage::{ BlockMarkerId, BranchSpan, ConditionId, ConditionInfo, CoverageInfoHi, CoverageKind, }; @@ -128,7 +128,7 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>( } impl ExtractedMappings { - pub(super) fn all_bcbs_with_counter_mappings(&self) -> BitSet { + pub(super) fn all_bcbs_with_counter_mappings(&self) -> DenseBitSet { // Fully destructure self to make sure we don't miss any fields that have mappings. let Self { num_bcbs, @@ -140,7 +140,7 @@ impl ExtractedMappings { } = self; // Identify which BCBs have one or more mappings. - let mut bcbs_with_counter_mappings = BitSet::new_empty(*num_bcbs); + let mut bcbs_with_counter_mappings = DenseBitSet::new_empty(*num_bcbs); let mut insert = |bcb| { bcbs_with_counter_mappings.insert(bcb); }; @@ -172,8 +172,8 @@ impl ExtractedMappings { } /// Returns the set of BCBs that have one or more `Code` mappings. - pub(super) fn bcbs_with_ordinary_code_mappings(&self) -> BitSet { - let mut bcbs = BitSet::new_empty(self.num_bcbs); + pub(super) fn bcbs_with_ordinary_code_mappings(&self) -> DenseBitSet { + let mut bcbs = DenseBitSet::new_empty(self.num_bcbs); for &CodeMapping { span: _, bcb } in &self.code_mappings { bcbs.insert(bcb); } @@ -367,9 +367,8 @@ fn calc_test_vectors_index(conditions: &mut Vec) -> usize { }) .collect::>(); - let mut queue = std::collections::VecDeque::from_iter( - next_conditions.swap_remove(&ConditionId::START).into_iter(), - ); + let mut queue = + std::collections::VecDeque::from_iter(next_conditions.swap_remove(&ConditionId::START)); num_paths_stats[ConditionId::START] = 1; let mut decision_end_nodes = Vec::new(); while let Some(branch) = queue.pop_front() { diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index edaec3c79651..3e7cf8541c23 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,5 +1,5 @@ use rustc_data_structures::captures::Captures; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::{ CounterId, CovTerm, CoverageIdsInfo, CoverageKind, Expression, ExpressionId, @@ -92,13 +92,13 @@ fn coverage_ids_info<'tcx>( let Some(fn_cov_info) = mir_body.function_coverage_info.as_deref() else { return CoverageIdsInfo { - counters_seen: BitSet::new_empty(0), - zero_expressions: BitSet::new_empty(0), + counters_seen: DenseBitSet::new_empty(0), + zero_expressions: DenseBitSet::new_empty(0), }; }; - let mut counters_seen = BitSet::new_empty(fn_cov_info.num_counters); - let mut expressions_seen = BitSet::new_filled(fn_cov_info.expressions.len()); + let mut counters_seen = DenseBitSet::new_empty(fn_cov_info.num_counters); + let mut expressions_seen = DenseBitSet::new_filled(fn_cov_info.expressions.len()); // For each expression ID that is directly used by one or more mappings, // mark it as not-yet-seen. This indicates that we expect to see a @@ -148,23 +148,23 @@ fn is_inlined(body: &Body<'_>, statement: &Statement<'_>) -> bool { scope_data.inlined.is_some() || scope_data.inlined_parent_scope.is_some() } -/// Identify expressions that will always have a value of zero, and note -/// their IDs in a `BitSet`. Mappings that refer to a zero expression -/// can instead become mappings to a constant zero value. +/// Identify expressions that will always have a value of zero, and note their +/// IDs in a `DenseBitSet`. Mappings that refer to a zero expression can instead +/// become mappings to a constant zero value. /// /// This function mainly exists to preserve the simplifications that were /// already being performed by the Rust-side expression renumbering, so that /// the resulting coverage mappings don't get worse. fn identify_zero_expressions( fn_cov_info: &FunctionCoverageInfo, - counters_seen: &BitSet, - expressions_seen: &BitSet, -) -> BitSet { + counters_seen: &DenseBitSet, + expressions_seen: &DenseBitSet, +) -> DenseBitSet { // The set of expressions that either were optimized out entirely, or // have zero as both of their operands, and will therefore always have // a value of zero. Other expressions that refer to these as operands // can have those operands replaced with `CovTerm::Zero`. - let mut zero_expressions = BitSet::new_empty(fn_cov_info.expressions.len()); + let mut zero_expressions = DenseBitSet::new_empty(fn_cov_info.expressions.len()); // Simplify a copy of each expression based on lower-numbered expressions, // and then update the set of always-zero expressions if necessary. @@ -228,8 +228,8 @@ fn identify_zero_expressions( /// into account knowledge of which counters are unused and which expressions /// are always zero. fn is_zero_term( - counters_seen: &BitSet, - zero_expressions: &BitSet, + counters_seen: &DenseBitSet, + zero_expressions: &DenseBitSet, term: CovTerm, ) -> bool { match term { diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index e1f1dd83f0df..8fce856687cb 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -46,7 +46,7 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // #[inline(never)] to force code generation. match codegen_fn_attrs.inline { InlineAttr::Never => return false, - InlineAttr::Hint | InlineAttr::Always => return true, + InlineAttr::Hint | InlineAttr::Always | InlineAttr::Force { .. } => return true, _ => {} } @@ -69,8 +69,9 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // Don't do any inference if codegen optimizations are disabled and also MIR inlining is not // enabled. This ensures that we do inference even if someone only passes -Zinline-mir, // which is less confusing than having to also enable -Copt-level=1. - if matches!(tcx.sess.opts.optimize, OptLevel::No) && !pm::should_run_pass(tcx, &inline::Inline) - { + let inliner_will_run = pm::should_run_pass(tcx, &inline::Inline) + || inline::ForceInline::should_run_pass_for_callee(tcx, def_id.to_def_id()); + if matches!(tcx.sess.opts.optimize, OptLevel::No) && !inliner_will_run { return false; } diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 0c75cdadc92d..434e921d4392 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -26,8 +26,8 @@ use crate::util::is_within_packed; /// Performs the optimization on the body /// -/// The `borrowed` set must be a `BitSet` of all the locals that are ever borrowed in this body. It -/// can be generated via the [`borrowed_locals`] function. +/// The `borrowed` set must be a `DenseBitSet` of all the locals that are ever borrowed in this +/// body. It can be generated via the [`borrowed_locals`] function. fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let borrowed_locals = borrowed_locals(body); diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index 67b215c7c9d6..049f13ce96d7 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -6,7 +6,7 @@ //! dependent crates can use them. use rustc_hir::def_id::LocalDefId; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{Body, Location, Operand, Place, RETURN_PLACE, Terminator, TerminatorKind}; use rustc_middle::ty::{self, DeducedParamAttrs, Ty, TyCtxt}; @@ -18,13 +18,13 @@ struct DeduceReadOnly { /// Each bit is indexed by argument number, starting at zero (so 0 corresponds to local decl /// 1). The bit is true if the argument may have been mutated or false if we know it hasn't /// been up to the point we're at. - mutable_args: BitSet, + mutable_args: DenseBitSet, } impl DeduceReadOnly { /// Returns a new DeduceReadOnly instance. fn new(arg_count: usize) -> Self { - Self { mutable_args: BitSet::new_empty(arg_count) } + Self { mutable_args: DenseBitSet::new_empty(arg_count) } } } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index e99bee6a01f5..1ac4d835946f 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -132,7 +132,7 @@ //! [attempt 3]: https://github.com/rust-lang/rust/pull/72632 use rustc_data_structures::fx::{FxIndexMap, IndexEntry, IndexOccupiedEntry}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::SparseIntervalMatrix; use rustc_middle::bug; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; @@ -204,7 +204,8 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { // Because we only filter once per round, it is unsound to use a local for more than // one merge operation within a single round of optimizations. We store here which ones // we have already used. - let mut merged_locals: BitSet = BitSet::new_empty(body.local_decls.len()); + let mut merged_locals: DenseBitSet = + DenseBitSet::new_empty(body.local_decls.len()); // This is the set of merges we will apply this round. It is a subset of the candidates. let mut merges = FxIndexMap::default(); @@ -274,7 +275,7 @@ fn apply_merges<'tcx>( body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>, merges: FxIndexMap, - merged_locals: BitSet, + merged_locals: DenseBitSet, ) { let mut merger = Merger { tcx, merges, merged_locals }; merger.visit_body_preserves_cfg(body); @@ -283,7 +284,7 @@ fn apply_merges<'tcx>( struct Merger<'tcx> { tcx: TyCtxt<'tcx>, merges: FxIndexMap, - merged_locals: BitSet, + merged_locals: DenseBitSet, } impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { @@ -351,7 +352,7 @@ impl Candidates { /// Collects the candidates for merging. /// /// This is responsible for enforcing the first and third bullet point. - fn reset_and_find<'tcx>(&mut self, body: &Body<'tcx>, borrowed: &BitSet) { + fn reset_and_find<'tcx>(&mut self, body: &Body<'tcx>, borrowed: &DenseBitSet) { self.c.clear(); self.reverse.clear(); let mut visitor = FindAssignments { body, candidates: &mut self.c, borrowed }; @@ -735,7 +736,7 @@ fn places_to_candidate_pair<'tcx>( struct FindAssignments<'a, 'tcx> { body: &'a Body<'tcx>, candidates: &'a mut FxIndexMap>, - borrowed: &'a BitSet, + borrowed: &'a DenseBitSet, } impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> { 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/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 3ebc91137250..988f1a25561e 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -2,7 +2,7 @@ use std::fmt; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; @@ -96,10 +96,10 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops { fn compute_dead_unwinds<'a, 'tcx>( body: &'a Body<'tcx>, flow_inits: &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, -) -> BitSet { +) -> DenseBitSet { // We only need to do this pass once, because unwind edges can only // reach cleanup blocks, which can't have unwind edges themselves. - let mut dead_unwinds = BitSet::new_empty(body.basic_blocks.len()); + let mut dead_unwinds = DenseBitSet::new_empty(body.basic_blocks.len()); for (bb, bb_data) in body.basic_blocks.iter_enumerated() { let TerminatorKind::Drop { place, unwind: UnwindAction::Cleanup(_), .. } = bb_data.terminator().kind diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 2d9eeddea2e2..015633d145f1 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -4,8 +4,8 @@ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::mir::AssertKind; use rustc_middle::ty::TyCtxt; use rustc_session::lint::{self, Lint}; -use rustc_span::Span; use rustc_span::def_id::DefId; +use rustc_span::{Span, Symbol}; use crate::fluent_generated as fluent; @@ -142,3 +142,29 @@ pub(crate) struct MustNotSuspendReason { #[note(mir_transform_note2)] #[help] pub(crate) struct UndefinedTransmute; + +#[derive(Diagnostic)] +#[diag(mir_transform_force_inline)] +#[note] +pub(crate) struct ForceInlineFailure { + #[label(mir_transform_caller)] + pub caller_span: Span, + #[label(mir_transform_callee)] + pub callee_span: Span, + #[label(mir_transform_attr)] + pub attr_span: Span, + #[primary_span] + #[label(mir_transform_call)] + pub call_span: Span, + pub callee: String, + pub caller: String, + pub reason: &'static str, + #[subdiagnostic] + pub justification: Option, +} + +#[derive(Subdiagnostic)] +#[note(mir_transform_force_inline_justification)] +pub(crate) struct ForceInlineJustification { + pub sym: Symbol, +} diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 283ed94b6151..affc4cf0afc3 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -94,7 +94,7 @@ use rustc_const_eval::interpret::{ use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexVec, newtype_index}; use rustc_middle::bug; use rustc_middle::mir::interpret::GlobalAlloc; @@ -256,7 +256,7 @@ struct VnState<'body, 'tcx> { feature_unsized_locals: bool, ssa: &'body SsaLocals, dominators: Dominators, - reused_locals: BitSet, + reused_locals: DenseBitSet, } impl<'body, 'tcx> VnState<'body, 'tcx> { @@ -287,7 +287,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { feature_unsized_locals: tcx.features().unsized_locals(), ssa, dominators, - reused_locals: BitSet::new_empty(local_decls.len()), + reused_locals: DenseBitSet::new_empty(local_decls.len()), } } @@ -1366,57 +1366,108 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return self.new_opaque(); } - let mut was_updated = false; + let mut was_ever_updated = false; + loop { + let mut was_updated_this_iteration = false; - // If that cast just casts away the metadata again, - if let PtrToPtr = kind - && let Value::Aggregate(AggregateTy::RawPtr { data_pointer_ty, .. }, _, fields) = - self.get(value) - && let ty::RawPtr(to_pointee, _) = to.kind() - && to_pointee.is_sized(self.tcx, self.typing_env()) - { - from = *data_pointer_ty; - value = fields[0]; - was_updated = true; - if *data_pointer_ty == to { - return Some(fields[0]); + // Transmuting between raw pointers is just a pointer cast so long as + // they have the same metadata type (like `*const i32` <=> `*mut u64` + // or `*mut [i32]` <=> `*const [u64]`), including the common special + // case of `*const T` <=> `*mut T`. + if let Transmute = kind + && from.is_unsafe_ptr() + && to.is_unsafe_ptr() + && self.pointers_have_same_metadata(from, to) + { + *kind = PtrToPtr; + was_updated_this_iteration = true; } - } - // PtrToPtr-then-PtrToPtr can skip the intermediate step - if let PtrToPtr = kind - && let Value::Cast { kind: inner_kind, value: inner_value, from: inner_from, to: _ } = - *self.get(value) - && let PtrToPtr = inner_kind - { - from = inner_from; - value = inner_value; - was_updated = true; - if inner_from == to { - return Some(inner_value); + // If a cast just casts away the metadata again, then we can get it by + // casting the original thin pointer passed to `from_raw_parts` + if let PtrToPtr = kind + && let Value::Aggregate(AggregateTy::RawPtr { data_pointer_ty, .. }, _, fields) = + self.get(value) + && let ty::RawPtr(to_pointee, _) = to.kind() + && to_pointee.is_sized(self.tcx, self.typing_env()) + { + from = *data_pointer_ty; + value = fields[0]; + was_updated_this_iteration = true; + if *data_pointer_ty == to { + return Some(fields[0]); + } } - } - // PtrToPtr-then-Transmute can just transmute the original, so long as the - // PtrToPtr didn't change metadata (and thus the size of the pointer) - if let Transmute = kind - && let Value::Cast { - kind: PtrToPtr, + // Aggregate-then-Transmute can just transmute the original field value, + // so long as the bytes of a value from only from a single field. + if let Transmute = kind + && let Value::Aggregate(_aggregate_ty, variant_idx, field_values) = self.get(value) + && let Some((field_idx, field_ty)) = + self.value_is_all_in_one_field(from, *variant_idx) + { + from = field_ty; + value = field_values[field_idx.as_usize()]; + was_updated_this_iteration = true; + if field_ty == to { + return Some(value); + } + } + + // Various cast-then-cast cases can be simplified. + if let Value::Cast { + kind: inner_kind, value: inner_value, from: inner_from, to: inner_to, } = *self.get(value) - && self.pointers_have_same_metadata(inner_from, inner_to) - { - from = inner_from; - value = inner_value; - was_updated = true; - if inner_from == to { - return Some(inner_value); + { + let new_kind = match (inner_kind, *kind) { + // Even if there's a narrowing cast in here that's fine, because + // things like `*mut [i32] -> *mut i32 -> *const i32` and + // `*mut [i32] -> *const [i32] -> *const i32` can skip the middle in MIR. + (PtrToPtr, PtrToPtr) => Some(PtrToPtr), + // PtrToPtr-then-Transmute is fine so long as the pointer cast is identity: + // `*const T -> *mut T -> NonNull` is fine, but we need to check for narrowing + // to skip things like `*const [i32] -> *const i32 -> NonNull`. + (PtrToPtr, Transmute) + if self.pointers_have_same_metadata(inner_from, inner_to) => + { + Some(Transmute) + } + // Similarly, for Transmute-then-PtrToPtr. Note that we need to check different + // variables for their metadata, and thus this can't merge with the previous arm. + (Transmute, PtrToPtr) if self.pointers_have_same_metadata(from, to) => { + Some(Transmute) + } + // If would be legal to always do this, but we don't want to hide information + // from the backend that it'd otherwise be able to use for optimizations. + (Transmute, Transmute) + if !self.type_may_have_niche_of_interest_to_backend(inner_to) => + { + Some(Transmute) + } + _ => None, + }; + if let Some(new_kind) = new_kind { + *kind = new_kind; + from = inner_from; + value = inner_value; + was_updated_this_iteration = true; + if inner_from == to { + return Some(inner_value); + } + } + } + + if was_updated_this_iteration { + was_ever_updated = true; + } else { + break; } } - if was_updated && let Some(op) = self.try_as_operand(value, location) { + if was_ever_updated && let Some(op) = self.try_as_operand(value, location) { *operand = op; } @@ -1438,6 +1489,54 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { false } } + + /// Returns `false` if we know for sure that this type has no interesting niche, + /// and thus we can skip transmuting through it without worrying. + /// + /// The backend will emit `assume`s when transmuting between types with niches, + /// so we want to preserve `i32 -> char -> u32` so that that data is around, + /// but it's fine to skip whole-range-is-value steps like `A -> u32 -> B`. + fn type_may_have_niche_of_interest_to_backend(&self, ty: Ty<'tcx>) -> bool { + let Ok(layout) = self.ecx.layout_of(ty) else { + // If it's too generic or something, then assume it might be interesting later. + return true; + }; + + match layout.backend_repr { + BackendRepr::Uninhabited => true, + BackendRepr::Scalar(a) => !a.is_always_valid(&self.ecx), + BackendRepr::ScalarPair(a, b) => { + !a.is_always_valid(&self.ecx) || !b.is_always_valid(&self.ecx) + } + BackendRepr::Vector { .. } | BackendRepr::Memory { .. } => false, + } + } + + fn value_is_all_in_one_field( + &self, + ty: Ty<'tcx>, + variant: VariantIdx, + ) -> Option<(FieldIdx, Ty<'tcx>)> { + if let Ok(layout) = self.ecx.layout_of(ty) + && let abi::Variants::Single { index } = layout.variants + && index == variant + && let Some((field_idx, field_layout)) = layout.non_1zst_field(&self.ecx) + && layout.size == field_layout.size + { + // We needed to check the variant to avoid trying to read the tag + // field from an enum where no fields have variants, since that tag + // field isn't in the `Aggregate` from which we're getting values. + Some((FieldIdx::from_usize(field_idx), field_layout.ty)) + } else if let ty::Adt(adt, args) = ty.kind() + && adt.is_struct() + && adt.repr().transparent() + && let [single_field] = adt.non_enum_variant().fields.raw.as_slice() + { + Some((FieldIdx::ZERO, single_field.ty(self.tcx, args))) + } else { + None + } + } } fn op_to_prop_const<'tcx>( @@ -1615,7 +1714,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { struct StorageRemover<'tcx> { tcx: TyCtxt<'tcx>, - reused_locals: BitSet, + reused_locals: DenseBitSet, } impl<'tcx> MutVisitor<'tcx> for StorageRemover<'tcx> { diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs new file mode 100644 index 000000000000..ba8389bbe2ff --- /dev/null +++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs @@ -0,0 +1,56 @@ +//! Check if it's even possible to satisfy the 'where' clauses +//! for this item. +//! +//! It's possible to `#!feature(trivial_bounds)]` to write +//! a function with impossible to satisfy clauses, e.g.: +//! `fn foo() where String: Copy {}`. +//! +//! We don't usually need to worry about this kind of case, +//! since we would get a compilation error if the user tried +//! to call it. However, since we optimize even without any +//! calls to the function, we need to make sure that it even +//! makes sense to try to evaluate the body. +//! +//! If there are unsatisfiable where clauses, then all bets are +//! off, and we just give up. +//! +//! We manually filter the predicates, skipping anything that's not +//! "global". We are in a potentially generic context +//! (e.g. we are evaluating a function without instantiating generic +//! parameters, so this filtering serves two purposes: +//! +//! 1. We skip evaluating any predicates that we would +//! never be able prove are unsatisfiable (e.g. `` +//! 2. We avoid trying to normalize predicates involving generic +//! parameters (e.g. `::MyItem`). This can confuse +//! the normalization code (leading to cycle errors), since +//! it's usually never invoked in this way. + +use rustc_middle::mir::{Body, START_BLOCK, TerminatorKind}; +use rustc_middle::ty::{TyCtxt, TypeVisitableExt}; +use rustc_trait_selection::traits; +use tracing::trace; + +use crate::pass_manager::MirPass; + +pub(crate) struct ImpossiblePredicates; + +impl<'tcx> MirPass<'tcx> for ImpossiblePredicates { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let predicates = tcx + .predicates_of(body.source.def_id()) + .predicates + .iter() + .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); + if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) { + trace!("found unsatisfiable predicates for {:?}", body.source); + // Clear the body to only contain a single `unreachable` statement. + let bbs = body.basic_blocks.as_mut(); + bbs.raw.truncate(1); + bbs[START_BLOCK].statements.clear(); + bbs[START_BLOCK].terminator_mut().kind = TerminatorKind::Unreachable; + body.var_debug_info.clear(); + body.local_decls.raw.truncate(body.arg_count + 1); + } + } +} diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 339acbad6b90..470393c9ae1b 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -8,15 +8,14 @@ use rustc_attr_parsing::InlineAttr; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_index::Idx; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; -use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; use rustc_session::config::{DebugInfo, OptLevel}; use rustc_span::source_map::Spanned; -use rustc_span::sym; use tracing::{debug, instrument, trace, trace_span}; use crate::cost_checker::CostChecker; @@ -29,10 +28,6 @@ pub(crate) mod cycle; const TOP_DOWN_DEPTH_LIMIT: usize = 5; -// Made public so that `mir_drops_elaborated_and_const_checked` can be overridden -// by custom rustc drivers, running all the steps by themselves. See #114628. -pub struct Inline; - #[derive(Clone, Debug)] struct CallSite<'tcx> { callee: Instance<'tcx>, @@ -41,14 +36,12 @@ struct CallSite<'tcx> { source_info: SourceInfo, } +// Made public so that `mir_drops_elaborated_and_const_checked` can be overridden +// by custom rustc drivers, running all the steps by themselves. See #114628. +pub struct Inline; + impl<'tcx> crate::MirPass<'tcx> for Inline { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - // FIXME(#127234): Coverage instrumentation currently doesn't handle inlined - // MIR correctly when Modified Condition/Decision Coverage is enabled. - if sess.instrument_coverage_mcdc() { - return false; - } - if let Some(enabled) = sess.opts.unstable_opts.inline_mir { return enabled; } @@ -67,7 +60,7 @@ impl<'tcx> crate::MirPass<'tcx> for Inline { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let span = trace_span!("inline", body = %tcx.def_path_str(body.source.def_id())); let _guard = span.enter(); - if inline(tcx, body) { + if inline::>(tcx, body) { debug!("running simplify cfg on {:?}", body.source); simplify_cfg(body); deref_finder(tcx, body); @@ -75,47 +68,203 @@ impl<'tcx> crate::MirPass<'tcx> for Inline { } } -fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { - let def_id = body.source.def_id().expect_local(); +pub struct ForceInline; - // Only do inlining into fn bodies. - if !tcx.hir().body_owner_kind(def_id).is_fn_or_closure() { - return false; +impl ForceInline { + pub fn should_run_pass_for_callee<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { + matches!(tcx.codegen_fn_attrs(def_id).inline, InlineAttr::Force { .. }) } - if body.source.promoted.is_some() { - return false; - } - // Avoid inlining into coroutines, since their `optimized_mir` is used for layout computation, - // which can create a cycle, even when no attempt is made to inline the function in the other - // direction. - if body.coroutine.is_some() { - return false; - } - - let typing_env = body.typing_env(tcx); - let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id); - - let mut this = Inliner { - tcx, - typing_env, - codegen_fn_attrs, - history: Vec::new(), - changed: false, - caller_is_inline_forwarder: matches!( - codegen_fn_attrs.inline, - InlineAttr::Hint | InlineAttr::Always - ) && body_is_forwarder(body), - }; - let blocks = START_BLOCK..body.basic_blocks.next_index(); - this.process_blocks(body, blocks); - this.changed } -struct Inliner<'tcx> { +impl<'tcx> crate::MirPass<'tcx> for ForceInline { + fn is_enabled(&self, _: &rustc_session::Session) -> bool { + true + } + + fn can_be_overridden(&self) -> bool { + false + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let span = trace_span!("force_inline", body = %tcx.def_path_str(body.source.def_id())); + let _guard = span.enter(); + if inline::>(tcx, body) { + debug!("running simplify cfg on {:?}", body.source); + simplify_cfg(body); + deref_finder(tcx, body); + } + } +} + +trait Inliner<'tcx> { + fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &Body<'tcx>) -> Self; + + fn tcx(&self) -> TyCtxt<'tcx>; + fn typing_env(&self) -> ty::TypingEnv<'tcx>; + fn history(&self) -> &[DefId]; + fn caller_def_id(&self) -> DefId; + + /// Has the caller body been changed? + fn changed(self) -> bool; + + /// Should inlining happen for a given callee? + fn should_inline_for_callee(&self, def_id: DefId) -> bool; + + fn check_caller_mir_body(&self, body: &Body<'tcx>) -> bool; + + /// Returns inlining decision that is based on the examination of callee MIR body. + /// Assumes that codegen attributes have been checked for compatibility already. + fn check_callee_mir_body( + &self, + callsite: &CallSite<'tcx>, + callee_body: &Body<'tcx>, + callee_attrs: &CodegenFnAttrs, + ) -> Result<(), &'static str>; + + // How many callsites in a body are we allowed to inline? We need to limit this in order + // to prevent super-linear growth in MIR size. + fn inline_limit_for_block(&self) -> Option; + + /// Called when inlining succeeds. + fn on_inline_success( + &mut self, + callsite: &CallSite<'tcx>, + caller_body: &mut Body<'tcx>, + new_blocks: std::ops::Range, + ); + + /// Called when inlining failed or was not performed. + fn on_inline_failure(&self, callsite: &CallSite<'tcx>, reason: &'static str); + + /// Called when the inline limit for a body is reached. + fn on_inline_limit_reached(&self) -> bool; +} + +struct ForceInliner<'tcx> { tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - /// Caller codegen attributes. - codegen_fn_attrs: &'tcx CodegenFnAttrs, + /// `DefId` of caller. + def_id: DefId, + /// Stack of inlined instances. + /// We only check the `DefId` and not the args because we want to + /// avoid inlining cases of polymorphic recursion. + /// The number of `DefId`s is finite, so checking history is enough + /// to ensure that we do not loop endlessly while inlining. + history: Vec, + /// Indicates that the caller body has been modified. + changed: bool, +} + +impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> { + fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &Body<'tcx>) -> Self { + Self { tcx, typing_env: body.typing_env(tcx), def_id, history: Vec::new(), changed: false } + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn typing_env(&self) -> ty::TypingEnv<'tcx> { + self.typing_env + } + + fn history(&self) -> &[DefId] { + &self.history + } + + fn caller_def_id(&self) -> DefId { + self.def_id + } + + fn changed(self) -> bool { + self.changed + } + + fn should_inline_for_callee(&self, def_id: DefId) -> bool { + ForceInline::should_run_pass_for_callee(self.tcx(), def_id) + } + + fn check_caller_mir_body(&self, _: &Body<'tcx>) -> bool { + true + } + + #[instrument(level = "debug", skip(self, callee_body))] + fn check_callee_mir_body( + &self, + _: &CallSite<'tcx>, + callee_body: &Body<'tcx>, + callee_attrs: &CodegenFnAttrs, + ) -> Result<(), &'static str> { + if callee_body.tainted_by_errors.is_some() { + return Err("body has errors"); + } + + let caller_attrs = self.tcx().codegen_fn_attrs(self.caller_def_id()); + if callee_attrs.instruction_set != caller_attrs.instruction_set + && callee_body + .basic_blocks + .iter() + .any(|bb| matches!(bb.terminator().kind, TerminatorKind::InlineAsm { .. })) + { + // During the attribute checking stage we allow a callee with no + // instruction_set assigned to count as compatible with a function that does + // assign one. However, during this stage we require an exact match when any + // inline-asm is detected. LLVM will still possibly do an inline later on + // if the no-attribute function ends up with the same instruction set anyway. + Err("cannot move inline-asm across instruction sets") + } else { + Ok(()) + } + } + + fn inline_limit_for_block(&self) -> Option { + Some(usize::MAX) + } + + fn on_inline_success( + &mut self, + callsite: &CallSite<'tcx>, + caller_body: &mut Body<'tcx>, + new_blocks: std::ops::Range, + ) { + self.changed = true; + + self.history.push(callsite.callee.def_id()); + process_blocks(self, caller_body, new_blocks); + self.history.pop(); + } + + fn on_inline_failure(&self, callsite: &CallSite<'tcx>, reason: &'static str) { + let tcx = self.tcx(); + let InlineAttr::Force { attr_span, reason: justification } = + tcx.codegen_fn_attrs(callsite.callee.def_id()).inline + else { + bug!("called on item without required inlining"); + }; + + let call_span = callsite.source_info.span; + tcx.dcx().emit_err(crate::errors::ForceInlineFailure { + call_span, + attr_span, + caller_span: tcx.def_span(self.def_id), + caller: tcx.def_path_str(self.def_id), + callee_span: tcx.def_span(callsite.callee.def_id()), + callee: tcx.def_path_str(callsite.callee.def_id()), + reason, + justification: justification.map(|sym| crate::errors::ForceInlineJustification { sym }), + }); + } + + fn on_inline_limit_reached(&self) -> bool { + false + } +} + +struct NormalInliner<'tcx> { + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + /// `DefId` of caller. + def_id: DefId, /// Stack of inlined instances. /// We only check the `DefId` and not the args because we want to /// avoid inlining cases of polymorphic recursion. @@ -129,361 +278,78 @@ struct Inliner<'tcx> { caller_is_inline_forwarder: bool, } -impl<'tcx> Inliner<'tcx> { - fn process_blocks(&mut self, caller_body: &mut Body<'tcx>, blocks: Range) { - // How many callsites in this body are we allowed to inline? We need to limit this in order - // to prevent super-linear growth in MIR size - let inline_limit = match self.history.len() { - 0 => usize::MAX, - 1..=TOP_DOWN_DEPTH_LIMIT => 1, - _ => return, - }; - let mut inlined_count = 0; - for bb in blocks { - let bb_data = &caller_body[bb]; - if bb_data.is_cleanup { - continue; - } +impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> { + fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &Body<'tcx>) -> Self { + let typing_env = body.typing_env(tcx); + let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id); - let Some(callsite) = self.resolve_callsite(caller_body, bb, bb_data) else { - continue; - }; - - let span = trace_span!("process_blocks", %callsite.callee, ?bb); - let _guard = span.enter(); - - match self.try_inlining(caller_body, &callsite) { - Err(reason) => { - debug!("not-inlined {} [{}]", callsite.callee, reason); - } - Ok(new_blocks) => { - debug!("inlined {}", callsite.callee); - self.changed = true; - - self.history.push(callsite.callee.def_id()); - self.process_blocks(caller_body, new_blocks); - self.history.pop(); - - inlined_count += 1; - if inlined_count == inline_limit { - debug!("inline count reached"); - return; - } - } - } + Self { + tcx, + typing_env, + def_id, + history: Vec::new(), + changed: false, + caller_is_inline_forwarder: matches!( + codegen_fn_attrs.inline, + InlineAttr::Hint | InlineAttr::Always | InlineAttr::Force { .. } + ) && body_is_forwarder(body), } } - /// Attempts to inline a callsite into the caller body. When successful returns basic blocks - /// containing the inlined body. Otherwise returns an error describing why inlining didn't take - /// place. - fn try_inlining( - &self, - caller_body: &mut Body<'tcx>, - callsite: &CallSite<'tcx>, - ) -> Result, &'static str> { - self.check_mir_is_available(caller_body, callsite.callee)?; - - let callee_attrs = self.tcx.codegen_fn_attrs(callsite.callee.def_id()); - let cross_crate_inlinable = self.tcx.cross_crate_inlinable(callsite.callee.def_id()); - self.check_codegen_attributes(callsite, callee_attrs, cross_crate_inlinable)?; - - // Intrinsic fallback bodies are automatically made cross-crate inlineable, - // but at this stage we don't know whether codegen knows the intrinsic, - // so just conservatively don't inline it. This also ensures that we do not - // accidentally inline the body of an intrinsic that *must* be overridden. - if self.tcx.has_attr(callsite.callee.def_id(), sym::rustc_intrinsic) { - return Err("Callee is an intrinsic, do not inline fallback bodies"); - } - - let terminator = caller_body[callsite.block].terminator.as_ref().unwrap(); - let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() }; - let destination_ty = destination.ty(&caller_body.local_decls, self.tcx).ty; - for arg in args { - if !arg.node.ty(&caller_body.local_decls, self.tcx).is_sized(self.tcx, self.typing_env) - { - // We do not allow inlining functions with unsized params. Inlining these functions - // could create unsized locals, which are unsound and being phased out. - return Err("Call has unsized argument"); - } - } - - let callee_body = try_instance_mir(self.tcx, callsite.callee.def)?; - self.check_mir_body(callsite, callee_body, callee_attrs, cross_crate_inlinable)?; - - let Ok(callee_body) = callsite.callee.try_instantiate_mir_and_normalize_erasing_regions( - self.tcx, - self.typing_env, - ty::EarlyBinder::bind(callee_body.clone()), - ) else { - return Err("failed to normalize callee body"); - }; - - // Normally, this shouldn't be required, but trait normalization failure can create a - // validation ICE. - if !validate_types(self.tcx, self.typing_env, &callee_body, &caller_body).is_empty() { - return Err("failed to validate callee body"); - } - - // Check call signature compatibility. - // Normally, this shouldn't be required, but trait normalization failure can create a - // validation ICE. - let output_type = callee_body.return_ty(); - if !util::sub_types(self.tcx, self.typing_env, output_type, destination_ty) { - trace!(?output_type, ?destination_ty); - return Err("failed to normalize return type"); - } - if callsite.fn_sig.abi() == ExternAbi::RustCall { - // FIXME: Don't inline user-written `extern "rust-call"` functions, - // since this is generally perf-negative on rustc, and we hope that - // LLVM will inline these functions instead. - if callee_body.spread_arg.is_some() { - return Err("do not inline user-written rust-call functions"); - } - - let (self_arg, arg_tuple) = match &args[..] { - [arg_tuple] => (None, arg_tuple), - [self_arg, arg_tuple] => (Some(self_arg), arg_tuple), - _ => bug!("Expected `rust-call` to have 1 or 2 args"), - }; - - let self_arg_ty = - self_arg.map(|self_arg| self_arg.node.ty(&caller_body.local_decls, self.tcx)); - - let arg_tuple_ty = arg_tuple.node.ty(&caller_body.local_decls, self.tcx); - let ty::Tuple(arg_tuple_tys) = *arg_tuple_ty.kind() else { - bug!("Closure arguments are not passed as a tuple"); - }; - - for (arg_ty, input) in - self_arg_ty.into_iter().chain(arg_tuple_tys).zip(callee_body.args_iter()) - { - let input_type = callee_body.local_decls[input].ty; - if !util::sub_types(self.tcx, self.typing_env, input_type, arg_ty) { - trace!(?arg_ty, ?input_type); - return Err("failed to normalize tuple argument type"); - } - } - } else { - for (arg, input) in args.iter().zip(callee_body.args_iter()) { - let input_type = callee_body.local_decls[input].ty; - let arg_ty = arg.node.ty(&caller_body.local_decls, self.tcx); - if !util::sub_types(self.tcx, self.typing_env, input_type, arg_ty) { - trace!(?arg_ty, ?input_type); - return Err("failed to normalize argument type"); - } - } - } - - let old_blocks = caller_body.basic_blocks.next_index(); - self.inline_call(caller_body, callsite, callee_body); - let new_blocks = old_blocks..caller_body.basic_blocks.next_index(); - - Ok(new_blocks) + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx } - fn check_mir_is_available( - &self, - caller_body: &Body<'tcx>, - callee: Instance<'tcx>, - ) -> Result<(), &'static str> { - let caller_def_id = caller_body.source.def_id(); - let callee_def_id = callee.def_id(); - if callee_def_id == caller_def_id { - return Err("self-recursion"); - } - - match callee.def { - InstanceKind::Item(_) => { - // If there is no MIR available (either because it was not in metadata or - // because it has no MIR because it's an extern function), then the inliner - // won't cause cycles on this. - if !self.tcx.is_mir_available(callee_def_id) { - return Err("item MIR unavailable"); - } - } - // These have no own callable MIR. - InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => { - return Err("instance without MIR (intrinsic / virtual)"); - } - - // FIXME(#127030): `ConstParamHasTy` has bad interactions with - // the drop shim builder, which does not evaluate predicates in - // the correct param-env for types being dropped. Stall resolving - // the MIR for this instance until all of its const params are - // substituted. - InstanceKind::DropGlue(_, Some(ty)) if ty.has_type_flags(TypeFlags::HAS_CT_PARAM) => { - return Err("still needs substitution"); - } - - // This cannot result in an immediate cycle since the callee MIR is a shim, which does - // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we - // do not need to catch this here, we can wait until the inliner decides to continue - // inlining a second time. - InstanceKind::VTableShim(_) - | InstanceKind::ReifyShim(..) - | InstanceKind::FnPtrShim(..) - | InstanceKind::ClosureOnceShim { .. } - | InstanceKind::ConstructCoroutineInClosureShim { .. } - | InstanceKind::DropGlue(..) - | InstanceKind::CloneShim(..) - | InstanceKind::ThreadLocalShim(..) - | InstanceKind::FnPtrAddrShim(..) - | InstanceKind::AsyncDropGlueCtorShim(..) => return Ok(()), - } - - if self.tcx.is_constructor(callee_def_id) { - trace!("constructors always have MIR"); - // Constructor functions cannot cause a query cycle. - return Ok(()); - } - - if callee_def_id.is_local() { - // If we know for sure that the function we're calling will itself try to - // call us, then we avoid inlining that function. - if self.tcx.mir_callgraph_reachable((callee, caller_def_id.expect_local())) { - return Err("caller might be reachable from callee (query cycle avoidance)"); - } - - Ok(()) - } else { - // This cannot result in an immediate cycle since the callee MIR is from another crate - // and is already optimized. Any subsequent inlining may cause cycles, but we do - // not need to catch this here, we can wait until the inliner decides to continue - // inlining a second time. - trace!("functions from other crates always have MIR"); - Ok(()) - } + fn caller_def_id(&self) -> DefId { + self.def_id } - fn resolve_callsite( - &self, - caller_body: &Body<'tcx>, - bb: BasicBlock, - bb_data: &BasicBlockData<'tcx>, - ) -> Option> { - // Only consider direct calls to functions - let terminator = bb_data.terminator(); - - // FIXME(explicit_tail_calls): figure out if we can inline tail calls - if let TerminatorKind::Call { ref func, fn_span, .. } = terminator.kind { - let func_ty = func.ty(caller_body, self.tcx); - if let ty::FnDef(def_id, args) = *func_ty.kind() { - // To resolve an instance its args have to be fully normalized. - let args = self.tcx.try_normalize_erasing_regions(self.typing_env, args).ok()?; - let callee = Instance::try_resolve(self.tcx, self.typing_env, def_id, args) - .ok() - .flatten()?; - - if let InstanceKind::Virtual(..) | InstanceKind::Intrinsic(_) = callee.def { - return None; - } - - if self.history.contains(&callee.def_id()) { - return None; - } - - let fn_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, args); - - // Additionally, check that the body that we're inlining actually agrees - // with the ABI of the trait that the item comes from. - if let InstanceKind::Item(instance_def_id) = callee.def - && self.tcx.def_kind(instance_def_id) == DefKind::AssocFn - && let instance_fn_sig = self.tcx.fn_sig(instance_def_id).skip_binder() - && instance_fn_sig.abi() != fn_sig.abi() - { - return None; - } - - let source_info = SourceInfo { span: fn_span, ..terminator.source_info }; - - return Some(CallSite { callee, fn_sig, block: bb, source_info }); - } - } - - None + fn typing_env(&self) -> ty::TypingEnv<'tcx> { + self.typing_env } - /// Returns an error if inlining is not possible based on codegen attributes alone. A success - /// indicates that inlining decision should be based on other criteria. - fn check_codegen_attributes( - &self, - callsite: &CallSite<'tcx>, - callee_attrs: &CodegenFnAttrs, - cross_crate_inlinable: bool, - ) -> Result<(), &'static str> { - if self.tcx.has_attr(callsite.callee.def_id(), sym::rustc_no_mir_inline) { - return Err("#[rustc_no_mir_inline]"); - } - - if let InlineAttr::Never = callee_attrs.inline { - return Err("never inline hint"); - } - - // Reachability pass defines which functions are eligible for inlining. Generally inlining - // other functions is incorrect because they could reference symbols that aren't exported. - let is_generic = callsite.callee.args.non_erasable_generics().next().is_some(); - if !is_generic && !cross_crate_inlinable { - return Err("not exported"); - } - - if callsite.fn_sig.c_variadic() { - return Err("C variadic"); - } - - if callee_attrs.flags.contains(CodegenFnAttrFlags::COLD) { - return Err("cold"); - } - - if callee_attrs.no_sanitize != self.codegen_fn_attrs.no_sanitize { - return Err("incompatible sanitizer set"); - } - - // Two functions are compatible if the callee has no attribute (meaning - // that it's codegen agnostic), or sets an attribute that is identical - // to this function's attribute. - if callee_attrs.instruction_set.is_some() - && callee_attrs.instruction_set != self.codegen_fn_attrs.instruction_set - { - return Err("incompatible instruction set"); - } - - let callee_feature_names = callee_attrs.target_features.iter().map(|f| f.name); - let this_feature_names = self.codegen_fn_attrs.target_features.iter().map(|f| f.name); - if callee_feature_names.ne(this_feature_names) { - // In general it is not correct to inline a callee with target features that are a - // subset of the caller. This is because the callee might contain calls, and the ABI of - // those calls depends on the target features of the surrounding function. By moving a - // `Call` terminator from one MIR body to another with more target features, we might - // change the ABI of that call! - return Err("incompatible target features"); - } - - Ok(()) + fn history(&self) -> &[DefId] { + &self.history + } + + fn changed(self) -> bool { + self.changed + } + + fn should_inline_for_callee(&self, _: DefId) -> bool { + true + } + + fn check_caller_mir_body(&self, body: &Body<'tcx>) -> bool { + // Avoid inlining into coroutines, since their `optimized_mir` is used for layout computation, + // which can create a cycle, even when no attempt is made to inline the function in the other + // direction. + if body.coroutine.is_some() { + return false; + } + + true } - /// Returns inlining decision that is based on the examination of callee MIR body. - /// Assumes that codegen attributes have been checked for compatibility already. #[instrument(level = "debug", skip(self, callee_body))] - fn check_mir_body( + fn check_callee_mir_body( &self, callsite: &CallSite<'tcx>, callee_body: &Body<'tcx>, callee_attrs: &CodegenFnAttrs, - cross_crate_inlinable: bool, ) -> Result<(), &'static str> { - let tcx = self.tcx; + let tcx = self.tcx(); if let Some(_) = callee_body.tainted_by_errors { - return Err("Body is tainted"); + return Err("body has errors"); } let mut threshold = if self.caller_is_inline_forwarder { - self.tcx.sess.opts.unstable_opts.inline_mir_forwarder_threshold.unwrap_or(30) - } else if cross_crate_inlinable { - self.tcx.sess.opts.unstable_opts.inline_mir_hint_threshold.unwrap_or(100) + tcx.sess.opts.unstable_opts.inline_mir_forwarder_threshold.unwrap_or(30) + } else if tcx.cross_crate_inlinable(callsite.callee.def_id()) { + tcx.sess.opts.unstable_opts.inline_mir_hint_threshold.unwrap_or(100) } else { - self.tcx.sess.opts.unstable_opts.inline_mir_threshold.unwrap_or(50) + tcx.sess.opts.unstable_opts.inline_mir_threshold.unwrap_or(50) }; // Give a bonus functions with a small number of blocks, @@ -497,13 +363,13 @@ impl<'tcx> Inliner<'tcx> { // FIXME: Give a bonus to functions with only a single caller let mut checker = - CostChecker::new(self.tcx, self.typing_env, Some(callsite.callee), callee_body); + CostChecker::new(tcx, self.typing_env(), Some(callsite.callee), callee_body); checker.add_function_level_costs(); // Traverse the MIR manually so we can account for the effects of inlining on the CFG. let mut work_list = vec![START_BLOCK]; - let mut visited = BitSet::new_empty(callee_body.basic_blocks.len()); + let mut visited = DenseBitSet::new_empty(callee_body.basic_blocks.len()); while let Some(bb) = work_list.pop() { if !visited.insert(bb.index()) { continue; @@ -513,20 +379,20 @@ impl<'tcx> Inliner<'tcx> { checker.visit_basic_block_data(bb, blk); let term = blk.terminator(); + let caller_attrs = tcx.codegen_fn_attrs(self.caller_def_id()); if let TerminatorKind::Drop { ref place, target, unwind, replace: _ } = term.kind { work_list.push(target); // If the place doesn't actually need dropping, treat it like a regular goto. - let ty = callsite.callee.instantiate_mir( - self.tcx, - ty::EarlyBinder::bind(&place.ty(callee_body, tcx).ty), - ); - if ty.needs_drop(tcx, self.typing_env) + let ty = callsite + .callee + .instantiate_mir(tcx, ty::EarlyBinder::bind(&place.ty(callee_body, tcx).ty)); + if ty.needs_drop(tcx, self.typing_env()) && let UnwindAction::Cleanup(unwind) = unwind { work_list.push(unwind); } - } else if callee_attrs.instruction_set != self.codegen_fn_attrs.instruction_set + } else if callee_attrs.instruction_set != caller_attrs.instruction_set && matches!(term.kind, TerminatorKind::InlineAsm { .. }) { // During the attribute checking stage we allow a callee with no @@ -534,7 +400,7 @@ impl<'tcx> Inliner<'tcx> { // assign one. However, during this stage we require an exact match when any // inline-asm is detected. LLVM will still possibly do an inline later on // if the no-attribute function ends up with the same instruction set anyway. - return Err("Cannot move inline-asm across instruction sets"); + return Err("cannot move inline-asm across instruction sets"); } else if let TerminatorKind::TailCall { .. } = term.kind { // FIXME(explicit_tail_calls): figure out how exactly functions containing tail // calls can be inlined (and if they even should) @@ -558,323 +424,690 @@ impl<'tcx> Inliner<'tcx> { } } - fn inline_call( - &self, - caller_body: &mut Body<'tcx>, + fn inline_limit_for_block(&self) -> Option { + match self.history.len() { + 0 => Some(usize::MAX), + 1..=TOP_DOWN_DEPTH_LIMIT => Some(1), + _ => None, + } + } + + fn on_inline_success( + &mut self, callsite: &CallSite<'tcx>, - mut callee_body: Body<'tcx>, + caller_body: &mut Body<'tcx>, + new_blocks: std::ops::Range, ) { - let terminator = caller_body[callsite.block].terminator.take().unwrap(); - let TerminatorKind::Call { func, args, destination, unwind, target, .. } = terminator.kind - else { - bug!("unexpected terminator kind {:?}", terminator.kind); + self.changed = true; + + self.history.push(callsite.callee.def_id()); + process_blocks(self, caller_body, new_blocks); + self.history.pop(); + } + + fn on_inline_limit_reached(&self) -> bool { + true + } + + fn on_inline_failure(&self, _: &CallSite<'tcx>, _: &'static str) {} +} + +fn inline<'tcx, T: Inliner<'tcx>>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { + let def_id = body.source.def_id(); + + // Only do inlining into fn bodies. + if !tcx.hir().body_owner_kind(def_id).is_fn_or_closure() { + return false; + } + + let mut inliner = T::new(tcx, def_id, body); + if !inliner.check_caller_mir_body(body) { + return false; + } + + let blocks = START_BLOCK..body.basic_blocks.next_index(); + process_blocks(&mut inliner, body, blocks); + inliner.changed() +} + +fn process_blocks<'tcx, I: Inliner<'tcx>>( + inliner: &mut I, + caller_body: &mut Body<'tcx>, + blocks: Range, +) { + let Some(inline_limit) = inliner.inline_limit_for_block() else { return }; + let mut inlined_count = 0; + for bb in blocks { + let bb_data = &caller_body[bb]; + if bb_data.is_cleanup { + continue; + } + + let Some(callsite) = resolve_callsite(inliner, caller_body, bb, bb_data) else { + continue; }; - let return_block = if let Some(block) = target { - // Prepare a new block for code that should execute when call returns. We don't use - // target block directly since it might have other predecessors. - let data = BasicBlockData::new( - Some(Terminator { - source_info: terminator.source_info, - kind: TerminatorKind::Goto { target: block }, - }), - caller_body[block].is_cleanup, - ); - Some(caller_body.basic_blocks_mut().push(data)) - } else { - None - }; + let span = trace_span!("process_blocks", %callsite.callee, ?bb); + let _guard = span.enter(); - // If the call is something like `a[*i] = f(i)`, where - // `i : &mut usize`, then just duplicating the `a[*i]` - // Place could result in two different locations if `f` - // writes to `i`. To prevent this we need to create a temporary - // borrow of the place and pass the destination as `*temp` instead. - fn dest_needs_borrow(place: Place<'_>) -> bool { - for elem in place.projection.iter() { - match elem { - ProjectionElem::Deref | ProjectionElem::Index(_) => return true, - _ => {} + match try_inlining(inliner, caller_body, &callsite) { + Err(reason) => { + debug!("not-inlined {} [{}]", callsite.callee, reason); + inliner.on_inline_failure(&callsite, reason); + } + Ok(new_blocks) => { + debug!("inlined {}", callsite.callee); + inliner.on_inline_success(&callsite, caller_body, new_blocks); + + inlined_count += 1; + if inlined_count == inline_limit { + if inliner.on_inline_limit_reached() { + return; + } } } + } + } +} - false +fn resolve_callsite<'tcx, I: Inliner<'tcx>>( + inliner: &I, + caller_body: &Body<'tcx>, + bb: BasicBlock, + bb_data: &BasicBlockData<'tcx>, +) -> Option> { + let tcx = inliner.tcx(); + // Only consider direct calls to functions + let terminator = bb_data.terminator(); + + // FIXME(explicit_tail_calls): figure out if we can inline tail calls + if let TerminatorKind::Call { ref func, fn_span, .. } = terminator.kind { + let func_ty = func.ty(caller_body, tcx); + if let ty::FnDef(def_id, args) = *func_ty.kind() { + if !inliner.should_inline_for_callee(def_id) { + debug!("not enabled"); + return None; + } + + // To resolve an instance its args have to be fully normalized. + let args = tcx.try_normalize_erasing_regions(inliner.typing_env(), args).ok()?; + let callee = + Instance::try_resolve(tcx, inliner.typing_env(), def_id, args).ok().flatten()?; + + if let InstanceKind::Virtual(..) | InstanceKind::Intrinsic(_) = callee.def { + return None; + } + + if inliner.history().contains(&callee.def_id()) { + return None; + } + + let fn_sig = tcx.fn_sig(def_id).instantiate(tcx, args); + + // Additionally, check that the body that we're inlining actually agrees + // with the ABI of the trait that the item comes from. + if let InstanceKind::Item(instance_def_id) = callee.def + && tcx.def_kind(instance_def_id) == DefKind::AssocFn + && let instance_fn_sig = tcx.fn_sig(instance_def_id).skip_binder() + && instance_fn_sig.abi() != fn_sig.abi() + { + return None; + } + + let source_info = SourceInfo { span: fn_span, ..terminator.source_info }; + + return Some(CallSite { callee, fn_sig, block: bb, source_info }); + } + } + + None +} + +/// Attempts to inline a callsite into the caller body. When successful returns basic blocks +/// containing the inlined body. Otherwise returns an error describing why inlining didn't take +/// place. +fn try_inlining<'tcx, I: Inliner<'tcx>>( + inliner: &I, + caller_body: &mut Body<'tcx>, + callsite: &CallSite<'tcx>, +) -> Result, &'static str> { + let tcx = inliner.tcx(); + check_mir_is_available(inliner, caller_body, callsite.callee)?; + + let callee_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id()); + rustc_mir_build::check_inline::is_inline_valid_on_fn(tcx, callsite.callee.def_id())?; + check_codegen_attributes(inliner, callsite, callee_attrs)?; + + let terminator = caller_body[callsite.block].terminator.as_ref().unwrap(); + let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() }; + let destination_ty = destination.ty(&caller_body.local_decls, tcx).ty; + for arg in args { + if !arg.node.ty(&caller_body.local_decls, tcx).is_sized(tcx, inliner.typing_env()) { + // We do not allow inlining functions with unsized params. Inlining these functions + // could create unsized locals, which are unsound and being phased out. + return Err("call has unsized argument"); + } + } + + let callee_body = try_instance_mir(tcx, callsite.callee.def)?; + rustc_mir_build::check_inline::is_inline_valid_on_body(tcx, callee_body)?; + inliner.check_callee_mir_body(callsite, callee_body, callee_attrs)?; + + let Ok(callee_body) = callsite.callee.try_instantiate_mir_and_normalize_erasing_regions( + tcx, + inliner.typing_env(), + ty::EarlyBinder::bind(callee_body.clone()), + ) else { + debug!("failed to normalize callee body"); + return Err("implementation limitation"); + }; + + // Normally, this shouldn't be required, but trait normalization failure can create a + // validation ICE. + if !validate_types(tcx, inliner.typing_env(), &callee_body, &caller_body).is_empty() { + debug!("failed to validate callee body"); + return Err("implementation limitation"); + } + + // Check call signature compatibility. + // Normally, this shouldn't be required, but trait normalization failure can create a + // validation ICE. + let output_type = callee_body.return_ty(); + if !util::sub_types(tcx, inliner.typing_env(), output_type, destination_ty) { + trace!(?output_type, ?destination_ty); + debug!("failed to normalize return type"); + return Err("implementation limitation"); + } + if callsite.fn_sig.abi() == ExternAbi::RustCall { + // FIXME: Don't inline user-written `extern "rust-call"` functions, + // since this is generally perf-negative on rustc, and we hope that + // LLVM will inline these functions instead. + if callee_body.spread_arg.is_some() { + return Err("user-written rust-call functions"); } - let dest = if dest_needs_borrow(destination) { - trace!("creating temp for return destination"); - let dest = Rvalue::Ref( - self.tcx.lifetimes.re_erased, - BorrowKind::Mut { kind: MutBorrowKind::Default }, - destination, - ); - let dest_ty = dest.ty(caller_body, self.tcx); - let temp = - Place::from(self.new_call_temp(caller_body, callsite, dest_ty, return_block)); - caller_body[callsite.block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::Assign(Box::new((temp, dest))), - }); - self.tcx.mk_place_deref(temp) - } else { - destination + let (self_arg, arg_tuple) = match &args[..] { + [arg_tuple] => (None, arg_tuple), + [self_arg, arg_tuple] => (Some(self_arg), arg_tuple), + _ => bug!("Expected `rust-call` to have 1 or 2 args"), }; - // Always create a local to hold the destination, as `RETURN_PLACE` may appear - // where a full `Place` is not allowed. - let (remap_destination, destination_local) = if let Some(d) = dest.as_local() { - (false, d) - } else { - ( - true, - self.new_call_temp( - caller_body, - callsite, - destination.ty(caller_body, self.tcx).ty, - return_block, - ), - ) + let self_arg_ty = self_arg.map(|self_arg| self_arg.node.ty(&caller_body.local_decls, tcx)); + + let arg_tuple_ty = arg_tuple.node.ty(&caller_body.local_decls, tcx); + let ty::Tuple(arg_tuple_tys) = *arg_tuple_ty.kind() else { + bug!("Closure arguments are not passed as a tuple"); }; - // Copy the arguments if needed. - let args = self.make_call_args(args, callsite, caller_body, &callee_body, return_block); - - let mut integrator = Integrator { - args: &args, - new_locals: Local::new(caller_body.local_decls.len()).., - new_scopes: SourceScope::new(caller_body.source_scopes.len()).., - new_blocks: BasicBlock::new(caller_body.basic_blocks.len()).., - destination: destination_local, - callsite_scope: caller_body.source_scopes[callsite.source_info.scope].clone(), - callsite, - cleanup_block: unwind, - in_cleanup_block: false, - return_block, - tcx: self.tcx, - always_live_locals: BitSet::new_filled(callee_body.local_decls.len()), - }; - - // Map all `Local`s, `SourceScope`s and `BasicBlock`s to new ones - // (or existing ones, in a few special cases) in the caller. - integrator.visit_body(&mut callee_body); - - // If there are any locals without storage markers, give them storage only for the - // duration of the call. - for local in callee_body.vars_and_temps_iter() { - if integrator.always_live_locals.contains(local) { - let new_local = integrator.map_local(local); - caller_body[callsite.block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::StorageLive(new_local), - }); + for (arg_ty, input) in + self_arg_ty.into_iter().chain(arg_tuple_tys).zip(callee_body.args_iter()) + { + let input_type = callee_body.local_decls[input].ty; + if !util::sub_types(tcx, inliner.typing_env(), input_type, arg_ty) { + trace!(?arg_ty, ?input_type); + debug!("failed to normalize tuple argument type"); + return Err("implementation limitation"); } } - if let Some(block) = return_block { - // To avoid repeated O(n) insert, push any new statements to the end and rotate - // the slice once. - let mut n = 0; - if remap_destination { + } else { + for (arg, input) in args.iter().zip(callee_body.args_iter()) { + let input_type = callee_body.local_decls[input].ty; + let arg_ty = arg.node.ty(&caller_body.local_decls, tcx); + if !util::sub_types(tcx, inliner.typing_env(), input_type, arg_ty) { + trace!(?arg_ty, ?input_type); + debug!("failed to normalize argument type"); + return Err("implementation limitation"); + } + } + } + + let old_blocks = caller_body.basic_blocks.next_index(); + inline_call(inliner, caller_body, callsite, callee_body); + let new_blocks = old_blocks..caller_body.basic_blocks.next_index(); + + Ok(new_blocks) +} + +fn check_mir_is_available<'tcx, I: Inliner<'tcx>>( + inliner: &I, + caller_body: &Body<'tcx>, + callee: Instance<'tcx>, +) -> Result<(), &'static str> { + let caller_def_id = caller_body.source.def_id(); + let callee_def_id = callee.def_id(); + if callee_def_id == caller_def_id { + return Err("self-recursion"); + } + + match callee.def { + InstanceKind::Item(_) => { + // If there is no MIR available (either because it was not in metadata or + // because it has no MIR because it's an extern function), then the inliner + // won't cause cycles on this. + if !inliner.tcx().is_mir_available(callee_def_id) { + debug!("item MIR unavailable"); + return Err("implementation limitation"); + } + } + // These have no own callable MIR. + InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => { + debug!("instance without MIR (intrinsic / virtual)"); + return Err("implementation limitation"); + } + + // FIXME(#127030): `ConstParamHasTy` has bad interactions with + // the drop shim builder, which does not evaluate predicates in + // the correct param-env for types being dropped. Stall resolving + // the MIR for this instance until all of its const params are + // substituted. + InstanceKind::DropGlue(_, Some(ty)) if ty.has_type_flags(TypeFlags::HAS_CT_PARAM) => { + debug!("still needs substitution"); + return Err("implementation limitation"); + } + + // This cannot result in an immediate cycle since the callee MIR is a shim, which does + // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we + // do not need to catch this here, we can wait until the inliner decides to continue + // inlining a second time. + InstanceKind::VTableShim(_) + | InstanceKind::ReifyShim(..) + | InstanceKind::FnPtrShim(..) + | InstanceKind::ClosureOnceShim { .. } + | InstanceKind::ConstructCoroutineInClosureShim { .. } + | InstanceKind::DropGlue(..) + | InstanceKind::CloneShim(..) + | InstanceKind::ThreadLocalShim(..) + | InstanceKind::FnPtrAddrShim(..) + | InstanceKind::AsyncDropGlueCtorShim(..) => return Ok(()), + } + + if inliner.tcx().is_constructor(callee_def_id) { + trace!("constructors always have MIR"); + // Constructor functions cannot cause a query cycle. + return Ok(()); + } + + if callee_def_id.is_local() + && !inliner + .tcx() + .is_lang_item(inliner.tcx().parent(caller_def_id), rustc_hir::LangItem::FnOnce) + { + // If we know for sure that the function we're calling will itself try to + // call us, then we avoid inlining that function. + if inliner.tcx().mir_callgraph_reachable((callee, caller_def_id.expect_local())) { + debug!("query cycle avoidance"); + return Err("caller might be reachable from callee"); + } + + Ok(()) + } else { + // This cannot result in an immediate cycle since the callee MIR is from another crate + // and is already optimized. Any subsequent inlining may cause cycles, but we do + // not need to catch this here, we can wait until the inliner decides to continue + // inlining a second time. + trace!("functions from other crates always have MIR"); + Ok(()) + } +} + +/// Returns an error if inlining is not possible based on codegen attributes alone. A success +/// indicates that inlining decision should be based on other criteria. +fn check_codegen_attributes<'tcx, I: Inliner<'tcx>>( + inliner: &I, + callsite: &CallSite<'tcx>, + callee_attrs: &CodegenFnAttrs, +) -> Result<(), &'static str> { + let tcx = inliner.tcx(); + if let InlineAttr::Never = callee_attrs.inline { + return Err("never inline attribute"); + } + + // Reachability pass defines which functions are eligible for inlining. Generally inlining + // other functions is incorrect because they could reference symbols that aren't exported. + let is_generic = callsite.callee.args.non_erasable_generics().next().is_some(); + if !is_generic && !tcx.cross_crate_inlinable(callsite.callee.def_id()) { + return Err("not exported"); + } + + let codegen_fn_attrs = tcx.codegen_fn_attrs(inliner.caller_def_id()); + if callee_attrs.no_sanitize != codegen_fn_attrs.no_sanitize { + return Err("incompatible sanitizer set"); + } + + // Two functions are compatible if the callee has no attribute (meaning + // that it's codegen agnostic), or sets an attribute that is identical + // to this function's attribute. + if callee_attrs.instruction_set.is_some() + && callee_attrs.instruction_set != codegen_fn_attrs.instruction_set + { + return Err("incompatible instruction set"); + } + + let callee_feature_names = callee_attrs.target_features.iter().map(|f| f.name); + let this_feature_names = codegen_fn_attrs.target_features.iter().map(|f| f.name); + if callee_feature_names.ne(this_feature_names) { + // In general it is not correct to inline a callee with target features that are a + // subset of the caller. This is because the callee might contain calls, and the ABI of + // those calls depends on the target features of the surrounding function. By moving a + // `Call` terminator from one MIR body to another with more target features, we might + // change the ABI of that call! + return Err("incompatible target features"); + } + + Ok(()) +} + +fn inline_call<'tcx, I: Inliner<'tcx>>( + inliner: &I, + caller_body: &mut Body<'tcx>, + callsite: &CallSite<'tcx>, + mut callee_body: Body<'tcx>, +) { + let tcx = inliner.tcx(); + let terminator = caller_body[callsite.block].terminator.take().unwrap(); + let TerminatorKind::Call { func, args, destination, unwind, target, .. } = terminator.kind + else { + bug!("unexpected terminator kind {:?}", terminator.kind); + }; + + let return_block = if let Some(block) = target { + // Prepare a new block for code that should execute when call returns. We don't use + // target block directly since it might have other predecessors. + let data = BasicBlockData::new( + Some(Terminator { + source_info: terminator.source_info, + kind: TerminatorKind::Goto { target: block }, + }), + caller_body[block].is_cleanup, + ); + Some(caller_body.basic_blocks_mut().push(data)) + } else { + None + }; + + // If the call is something like `a[*i] = f(i)`, where + // `i : &mut usize`, then just duplicating the `a[*i]` + // Place could result in two different locations if `f` + // writes to `i`. To prevent this we need to create a temporary + // borrow of the place and pass the destination as `*temp` instead. + fn dest_needs_borrow(place: Place<'_>) -> bool { + for elem in place.projection.iter() { + match elem { + ProjectionElem::Deref | ProjectionElem::Index(_) => return true, + _ => {} + } + } + + false + } + + let dest = if dest_needs_borrow(destination) { + trace!("creating temp for return destination"); + let dest = Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + destination, + ); + let dest_ty = dest.ty(caller_body, tcx); + let temp = Place::from(new_call_temp(caller_body, callsite, dest_ty, return_block)); + caller_body[callsite.block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::Assign(Box::new((temp, dest))), + }); + tcx.mk_place_deref(temp) + } else { + destination + }; + + // Always create a local to hold the destination, as `RETURN_PLACE` may appear + // where a full `Place` is not allowed. + let (remap_destination, destination_local) = if let Some(d) = dest.as_local() { + (false, d) + } else { + ( + true, + new_call_temp(caller_body, callsite, destination.ty(caller_body, tcx).ty, return_block), + ) + }; + + // Copy the arguments if needed. + let args = make_call_args(inliner, args, callsite, caller_body, &callee_body, return_block); + + let mut integrator = Integrator { + args: &args, + new_locals: Local::new(caller_body.local_decls.len()).., + new_scopes: SourceScope::new(caller_body.source_scopes.len()).., + new_blocks: BasicBlock::new(caller_body.basic_blocks.len()).., + destination: destination_local, + callsite_scope: caller_body.source_scopes[callsite.source_info.scope].clone(), + callsite, + cleanup_block: unwind, + in_cleanup_block: false, + return_block, + tcx, + always_live_locals: DenseBitSet::new_filled(callee_body.local_decls.len()), + }; + + // Map all `Local`s, `SourceScope`s and `BasicBlock`s to new ones + // (or existing ones, in a few special cases) in the caller. + integrator.visit_body(&mut callee_body); + + // If there are any locals without storage markers, give them storage only for the + // duration of the call. + for local in callee_body.vars_and_temps_iter() { + if integrator.always_live_locals.contains(local) { + let new_local = integrator.map_local(local); + caller_body[callsite.block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::StorageLive(new_local), + }); + } + } + if let Some(block) = return_block { + // To avoid repeated O(n) insert, push any new statements to the end and rotate + // the slice once. + let mut n = 0; + if remap_destination { + caller_body[block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::Assign(Box::new(( + dest, + Rvalue::Use(Operand::Move(destination_local.into())), + ))), + }); + n += 1; + } + for local in callee_body.vars_and_temps_iter().rev() { + if integrator.always_live_locals.contains(local) { + let new_local = integrator.map_local(local); caller_body[block].statements.push(Statement { source_info: callsite.source_info, - kind: StatementKind::Assign(Box::new(( - dest, - Rvalue::Use(Operand::Move(destination_local.into())), - ))), + kind: StatementKind::StorageDead(new_local), }); n += 1; } - for local in callee_body.vars_and_temps_iter().rev() { - if integrator.always_live_locals.contains(local) { - let new_local = integrator.map_local(local); - caller_body[block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::StorageDead(new_local), - }); - n += 1; - } - } - caller_body[block].statements.rotate_right(n); } + caller_body[block].statements.rotate_right(n); + } - // Insert all of the (mapped) parts of the callee body into the caller. - caller_body.local_decls.extend(callee_body.drain_vars_and_temps()); - caller_body.source_scopes.append(&mut callee_body.source_scopes); - if self - .tcx - .sess - .opts - .unstable_opts - .inline_mir_preserve_debug - .unwrap_or(self.tcx.sess.opts.debuginfo != DebugInfo::None) - { - // Note that we need to preserve these in the standard library so that - // people working on rust can build with or without debuginfo while - // still getting consistent results from the mir-opt tests. - caller_body.var_debug_info.append(&mut callee_body.var_debug_info); - } - caller_body.basic_blocks_mut().append(callee_body.basic_blocks_mut()); + // Insert all of the (mapped) parts of the callee body into the caller. + caller_body.local_decls.extend(callee_body.drain_vars_and_temps()); + caller_body.source_scopes.append(&mut callee_body.source_scopes); + if tcx + .sess + .opts + .unstable_opts + .inline_mir_preserve_debug + .unwrap_or(tcx.sess.opts.debuginfo != DebugInfo::None) + { + // Note that we need to preserve these in the standard library so that + // people working on rust can build with or without debuginfo while + // still getting consistent results from the mir-opt tests. + caller_body.var_debug_info.append(&mut callee_body.var_debug_info); + } + caller_body.basic_blocks_mut().append(callee_body.basic_blocks_mut()); - caller_body[callsite.block].terminator = Some(Terminator { - source_info: callsite.source_info, - kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) }, - }); + caller_body[callsite.block].terminator = Some(Terminator { + source_info: callsite.source_info, + kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) }, + }); - // Copy required constants from the callee_body into the caller_body. Although we are only - // pushing unevaluated consts to `required_consts`, here they may have been evaluated - // because we are calling `instantiate_and_normalize_erasing_regions` -- so we filter again. - caller_body.required_consts.as_mut().unwrap().extend( - callee_body.required_consts().into_iter().filter(|ct| ct.const_.is_required_const()), + // Copy required constants from the callee_body into the caller_body. Although we are only + // pushing unevaluated consts to `required_consts`, here they may have been evaluated + // because we are calling `instantiate_and_normalize_erasing_regions` -- so we filter again. + caller_body.required_consts.as_mut().unwrap().extend( + callee_body.required_consts().into_iter().filter(|ct| ct.const_.is_required_const()), + ); + // Now that we incorporated the callee's `required_consts`, we can remove the callee from + // `mentioned_items` -- but we have to take their `mentioned_items` in return. This does + // some extra work here to save the monomorphization collector work later. It helps a lot, + // since monomorphization can avoid a lot of work when the "mentioned items" are similar to + // the actually used items. By doing this we can entirely avoid visiting the callee! + // We need to reconstruct the `required_item` for the callee so that we can find and + // remove it. + let callee_item = MentionedItem::Fn(func.ty(caller_body, tcx)); + let caller_mentioned_items = caller_body.mentioned_items.as_mut().unwrap(); + if let Some(idx) = caller_mentioned_items.iter().position(|item| item.node == callee_item) { + // We found the callee, so remove it and add its items instead. + caller_mentioned_items.remove(idx); + caller_mentioned_items.extend(callee_body.mentioned_items()); + } else { + // If we can't find the callee, there's no point in adding its items. Probably it + // already got removed by being inlined elsewhere in the same function, so we already + // took its items. + } +} + +fn make_call_args<'tcx, I: Inliner<'tcx>>( + inliner: &I, + args: Box<[Spanned>]>, + callsite: &CallSite<'tcx>, + caller_body: &mut Body<'tcx>, + callee_body: &Body<'tcx>, + return_block: Option, +) -> Box<[Local]> { + let tcx = inliner.tcx(); + + // There is a bit of a mismatch between the *caller* of a closure and the *callee*. + // The caller provides the arguments wrapped up in a tuple: + // + // tuple_tmp = (a, b, c) + // Fn::call(closure_ref, tuple_tmp) + // + // meanwhile the closure body expects the arguments (here, `a`, `b`, and `c`) + // as distinct arguments. (This is the "rust-call" ABI hack.) Normally, codegen has + // the job of unpacking this tuple. But here, we are codegen. =) So we want to create + // a vector like + // + // [closure_ref, tuple_tmp.0, tuple_tmp.1, tuple_tmp.2] + // + // Except for one tiny wrinkle: we don't actually want `tuple_tmp.0`. It's more convenient + // if we "spill" that into *another* temporary, so that we can map the argument + // variable in the callee MIR directly to an argument variable on our side. + // So we introduce temporaries like: + // + // tmp0 = tuple_tmp.0 + // tmp1 = tuple_tmp.1 + // tmp2 = tuple_tmp.2 + // + // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`. + if callsite.fn_sig.abi() == ExternAbi::RustCall && callee_body.spread_arg.is_none() { + // FIXME(edition_2024): switch back to a normal method call. + let mut args = <_>::into_iter(args); + let self_ = create_temp_if_necessary( + inliner, + args.next().unwrap().node, + callsite, + caller_body, + return_block, ); - // Now that we incorporated the callee's `required_consts`, we can remove the callee from - // `mentioned_items` -- but we have to take their `mentioned_items` in return. This does - // some extra work here to save the monomorphization collector work later. It helps a lot, - // since monomorphization can avoid a lot of work when the "mentioned items" are similar to - // the actually used items. By doing this we can entirely avoid visiting the callee! - // We need to reconstruct the `required_item` for the callee so that we can find and - // remove it. - let callee_item = MentionedItem::Fn(func.ty(caller_body, self.tcx)); - let caller_mentioned_items = caller_body.mentioned_items.as_mut().unwrap(); - if let Some(idx) = caller_mentioned_items.iter().position(|item| item.node == callee_item) { - // We found the callee, so remove it and add its items instead. - caller_mentioned_items.remove(idx); - caller_mentioned_items.extend(callee_body.mentioned_items()); - } else { - // If we can't find the callee, there's no point in adding its items. Probably it - // already got removed by being inlined elsewhere in the same function, so we already - // took its items. - } - } + let tuple = create_temp_if_necessary( + inliner, + args.next().unwrap().node, + callsite, + caller_body, + return_block, + ); + assert!(args.next().is_none()); - fn make_call_args( - &self, - args: Box<[Spanned>]>, - callsite: &CallSite<'tcx>, - caller_body: &mut Body<'tcx>, - callee_body: &Body<'tcx>, - return_block: Option, - ) -> Box<[Local]> { - let tcx = self.tcx; + let tuple = Place::from(tuple); + let ty::Tuple(tuple_tys) = tuple.ty(caller_body, tcx).ty.kind() else { + bug!("Closure arguments are not passed as a tuple"); + }; - // There is a bit of a mismatch between the *caller* of a closure and the *callee*. - // The caller provides the arguments wrapped up in a tuple: - // - // tuple_tmp = (a, b, c) - // Fn::call(closure_ref, tuple_tmp) - // - // meanwhile the closure body expects the arguments (here, `a`, `b`, and `c`) - // as distinct arguments. (This is the "rust-call" ABI hack.) Normally, codegen has - // the job of unpacking this tuple. But here, we are codegen. =) So we want to create - // a vector like - // - // [closure_ref, tuple_tmp.0, tuple_tmp.1, tuple_tmp.2] - // - // Except for one tiny wrinkle: we don't actually want `tuple_tmp.0`. It's more convenient - // if we "spill" that into *another* temporary, so that we can map the argument - // variable in the callee MIR directly to an argument variable on our side. - // So we introduce temporaries like: - // - // tmp0 = tuple_tmp.0 - // tmp1 = tuple_tmp.1 - // tmp2 = tuple_tmp.2 - // - // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`. - if callsite.fn_sig.abi() == ExternAbi::RustCall && callee_body.spread_arg.is_none() { - // FIXME(edition_2024): switch back to a normal method call. - let mut args = <_>::into_iter(args); - let self_ = self.create_temp_if_necessary( - args.next().unwrap().node, - callsite, - caller_body, - return_block, - ); - let tuple = self.create_temp_if_necessary( - args.next().unwrap().node, - callsite, - caller_body, - return_block, - ); - assert!(args.next().is_none()); + // The `closure_ref` in our example above. + let closure_ref_arg = iter::once(self_); - let tuple = Place::from(tuple); - let ty::Tuple(tuple_tys) = tuple.ty(caller_body, tcx).ty.kind() else { - bug!("Closure arguments are not passed as a tuple"); - }; + // The `tmp0`, `tmp1`, and `tmp2` in our example above. + let tuple_tmp_args = tuple_tys.iter().enumerate().map(|(i, ty)| { + // This is e.g., `tuple_tmp.0` in our example above. + let tuple_field = Operand::Move(tcx.mk_place_field(tuple, FieldIdx::new(i), ty)); - // The `closure_ref` in our example above. - let closure_ref_arg = iter::once(self_); - - // The `tmp0`, `tmp1`, and `tmp2` in our example above. - let tuple_tmp_args = tuple_tys.iter().enumerate().map(|(i, ty)| { - // This is e.g., `tuple_tmp.0` in our example above. - let tuple_field = Operand::Move(tcx.mk_place_field(tuple, FieldIdx::new(i), ty)); - - // Spill to a local to make e.g., `tmp0`. - self.create_temp_if_necessary(tuple_field, callsite, caller_body, return_block) - }); - - closure_ref_arg.chain(tuple_tmp_args).collect() - } else { - // FIXME(edition_2024): switch back to a normal method call. - <_>::into_iter(args) - .map(|a| self.create_temp_if_necessary(a.node, callsite, caller_body, return_block)) - .collect() - } - } - - /// If `arg` is already a temporary, returns it. Otherwise, introduces a fresh - /// temporary `T` and an instruction `T = arg`, and returns `T`. - fn create_temp_if_necessary( - &self, - arg: Operand<'tcx>, - callsite: &CallSite<'tcx>, - caller_body: &mut Body<'tcx>, - return_block: Option, - ) -> Local { - // Reuse the operand if it is a moved temporary. - if let Operand::Move(place) = &arg - && let Some(local) = place.as_local() - && caller_body.local_kind(local) == LocalKind::Temp - { - return local; - } - - // Otherwise, create a temporary for the argument. - trace!("creating temp for argument {:?}", arg); - let arg_ty = arg.ty(caller_body, self.tcx); - let local = self.new_call_temp(caller_body, callsite, arg_ty, return_block); - caller_body[callsite.block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::Assign(Box::new((Place::from(local), Rvalue::Use(arg)))), - }); - local - } - - /// Introduces a new temporary into the caller body that is live for the duration of the call. - fn new_call_temp( - &self, - caller_body: &mut Body<'tcx>, - callsite: &CallSite<'tcx>, - ty: Ty<'tcx>, - return_block: Option, - ) -> Local { - let local = caller_body.local_decls.push(LocalDecl::new(ty, callsite.source_info.span)); - - caller_body[callsite.block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::StorageLive(local), + // Spill to a local to make e.g., `tmp0`. + create_temp_if_necessary(inliner, tuple_field, callsite, caller_body, return_block) }); - if let Some(block) = return_block { - caller_body[block].statements.insert(0, Statement { - source_info: callsite.source_info, - kind: StatementKind::StorageDead(local), - }); - } - - local + closure_ref_arg.chain(tuple_tmp_args).collect() + } else { + // FIXME(edition_2024): switch back to a normal method call. + <_>::into_iter(args) + .map(|a| create_temp_if_necessary(inliner, a.node, callsite, caller_body, return_block)) + .collect() } } +/// If `arg` is already a temporary, returns it. Otherwise, introduces a fresh temporary `T` and an +/// instruction `T = arg`, and returns `T`. +fn create_temp_if_necessary<'tcx, I: Inliner<'tcx>>( + inliner: &I, + arg: Operand<'tcx>, + callsite: &CallSite<'tcx>, + caller_body: &mut Body<'tcx>, + return_block: Option, +) -> Local { + // Reuse the operand if it is a moved temporary. + if let Operand::Move(place) = &arg + && let Some(local) = place.as_local() + && caller_body.local_kind(local) == LocalKind::Temp + { + return local; + } + + // Otherwise, create a temporary for the argument. + trace!("creating temp for argument {:?}", arg); + let arg_ty = arg.ty(caller_body, inliner.tcx()); + let local = new_call_temp(caller_body, callsite, arg_ty, return_block); + caller_body[callsite.block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::Assign(Box::new((Place::from(local), Rvalue::Use(arg)))), + }); + local +} + +/// Introduces a new temporary into the caller body that is live for the duration of the call. +fn new_call_temp<'tcx>( + caller_body: &mut Body<'tcx>, + callsite: &CallSite<'tcx>, + ty: Ty<'tcx>, + return_block: Option, +) -> Local { + let local = caller_body.local_decls.push(LocalDecl::new(ty, callsite.source_info.span)); + + caller_body[callsite.block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::StorageLive(local), + }); + + if let Some(block) = return_block { + caller_body[block].statements.insert(0, Statement { + source_info: callsite.source_info, + kind: StatementKind::StorageDead(local), + }); + } + + local +} + /** * Integrator. * @@ -894,7 +1127,7 @@ struct Integrator<'a, 'tcx> { in_cleanup_block: bool, return_block: Option, tcx: TyCtxt<'tcx>, - always_live_locals: BitSet, + always_live_locals: DenseBitSet, } impl Integrator<'_, '_> { diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 1a65affe8121..20e2e3e8ba2c 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -48,6 +48,8 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { ctx.simplify_ref_deref(rvalue); ctx.simplify_ptr_aggregate(rvalue); ctx.simplify_cast(rvalue); + ctx.simplify_repeated_aggregate(rvalue); + ctx.simplify_repeat_once(rvalue); } _ => {} } @@ -68,6 +70,35 @@ struct InstSimplifyContext<'a, 'tcx> { } impl<'tcx> InstSimplifyContext<'_, 'tcx> { + /// Transform aggregates like [0, 0, 0, 0, 0] into [0; 5]. + /// GVN can also do this optimization, but GVN is only run at mir-opt-level 2 so having this in + /// InstSimplify helps unoptimized builds. + fn simplify_repeated_aggregate(&self, rvalue: &mut Rvalue<'tcx>) { + let Rvalue::Aggregate(box AggregateKind::Array(_), fields) = rvalue else { + return; + }; + if fields.len() < 5 { + return; + } + let first = &fields[rustc_abi::FieldIdx::ZERO]; + let Operand::Constant(first) = first else { + return; + }; + let Ok(first_val) = first.const_.eval(self.tcx, self.typing_env, first.span) else { + return; + }; + if fields.iter().all(|field| { + let Operand::Constant(field) = field else { + return false; + }; + let field = field.const_.eval(self.tcx, self.typing_env, field.span); + field == Ok(first_val) + }) { + let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap()); + *rvalue = Rvalue::Repeat(Operand::Constant(first.clone()), len); + } + } + /// Transform boolean comparisons into logical operations. fn simplify_bool_cmp(&self, rvalue: &mut Rvalue<'tcx>) { match rvalue { @@ -173,33 +204,22 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { *kind = CastKind::IntToInt; return; } - - // Transmuting a transparent struct/union to a field's type is a projection - if let ty::Adt(adt_def, args) = operand_ty.kind() - && adt_def.repr().transparent() - && (adt_def.is_struct() || adt_def.is_union()) - && let Some(place) = operand.place() - { - let variant = adt_def.non_enum_variant(); - for (i, field) in variant.fields.iter_enumerated() { - let field_ty = field.ty(self.tcx, args); - if field_ty == *cast_ty { - let place = place - .project_deeper(&[ProjectionElem::Field(i, *cast_ty)], self.tcx); - let operand = if operand.is_move() { - Operand::Move(place) - } else { - Operand::Copy(place) - }; - *rvalue = Rvalue::Use(operand); - return; - } - } - } } } } + /// Simplify `[x; 1]` to just `[x]`. + fn simplify_repeat_once(&self, rvalue: &mut Rvalue<'tcx>) { + if let Rvalue::Repeat(operand, count) = rvalue + && let Some(1) = count.try_to_target_usize(self.tcx) + { + *rvalue = Rvalue::Aggregate( + Box::new(AggregateKind::Array(operand.ty(self.local_decls, self.tcx))), + [operand.clone()].into(), + ); + } + } + fn simplify_primitive_clone( &self, terminator: &mut Terminator<'tcx>, diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 8feb90ff7a06..c73a03489c57 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -40,7 +40,7 @@ use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::Visitor; @@ -121,7 +121,7 @@ struct TOFinder<'a, 'tcx> { ecx: InterpCx<'tcx, DummyMachine>, body: &'a Body<'tcx>, map: Map<'tcx>, - loop_headers: BitSet, + loop_headers: DenseBitSet, /// We use an arena to avoid cloning the slices when cloning `state`. arena: &'a DroplessArena, opportunities: Vec, @@ -832,8 +832,8 @@ enum Update { /// at least a predecessor which it dominates. This definition is only correct for reducible CFGs. /// But if the CFG is already irreducible, there is no point in trying much harder. /// is already irreducible. -fn loop_headers(body: &Body<'_>) -> BitSet { - let mut loop_headers = BitSet::new_empty(body.basic_blocks.len()); +fn loop_headers(body: &Body<'_>) -> DenseBitSet { + let mut loop_headers = DenseBitSet::new_empty(body.basic_blocks.len()); let dominators = body.basic_blocks.dominators(); // Only visit reachable blocks. for (bb, bbdata) in traversal::preorder(body) { diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index f1705d0c831c..9b3a0e672953 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -13,7 +13,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::HirId; use rustc_hir::def::DefKind; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -67,7 +67,7 @@ struct ConstPropagator<'mir, 'tcx> { tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, worklist: Vec, - visited_blocks: BitSet, + visited_blocks: DenseBitSet, locals: IndexVec>, body: &'mir Body<'tcx>, written_only_inside_own_block_locals: FxHashSet, @@ -190,7 +190,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { tcx, typing_env, worklist: vec![START_BLOCK], - visited_blocks: BitSet::new_empty(body.basic_blocks.len()), + visited_blocks: DenseBitSet::new_empty(body.basic_blocks.len()), locals: IndexVec::from_elem_n(Value::Uninit, body.local_decls.len()), body, can_const_prop, @@ -852,7 +852,7 @@ enum ConstPropMode { struct CanConstProp { can_const_prop: IndexVec, // False at the beginning. Once set, no more assignments are allowed to that local. - found_assignment: BitSet, + found_assignment: DenseBitSet, } impl CanConstProp { @@ -864,7 +864,7 @@ impl CanConstProp { ) -> IndexVec { let mut cpv = CanConstProp { can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls), - found_assignment: BitSet::new_empty(body.local_decls.len()), + found_assignment: DenseBitSet::new_empty(body.local_decls.len()), }; for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { let ty = body.local_decls[local].ty; diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index e1fba9be5bb6..9459ef99445c 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -34,8 +34,7 @@ use rustc_middle::util::Providers; use rustc_middle::{bug, query, span_bug}; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, sym}; -use rustc_trait_selection::traits; -use tracing::{debug, trace}; +use tracing::debug; #[macro_use] mod pass_manager; @@ -141,7 +140,8 @@ declare_passes! { mod gvn : GVN; // Made public so that `mir_drops_elaborated_and_const_checked` can be overridden // by custom rustc drivers, running all the steps by themselves. See #114628. - pub mod inline : Inline; + pub mod inline : Inline, ForceInline; + mod impossible_predicates : ImpossiblePredicates; mod instsimplify : InstSimplify { BeforeInline, AfterSimplifyCfg }; mod jump_threading : JumpThreading; mod known_panics_lint : KnownPanicsLint; @@ -488,7 +488,9 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & let is_fn_like = tcx.def_kind(def).is_fn_like(); if is_fn_like { // Do not compute the mir call graph without said call graph actually being used. - if pm::should_run_pass(tcx, &inline::Inline) { + if pm::should_run_pass(tcx, &inline::Inline) + || inline::ForceInline::should_run_pass_for_callee(tcx, def.to_def_id()) + { tcx.ensure_with_value().mir_inliner_callees(ty::InstanceKind::Item(def.to_def_id())); } } @@ -500,50 +502,6 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & body.tainted_by_errors = Some(error_reported); } - // Check if it's even possible to satisfy the 'where' clauses - // for this item. - // - // This branch will never be taken for any normal function. - // However, it's possible to `#!feature(trivial_bounds)]` to write - // a function with impossible to satisfy clauses, e.g.: - // `fn foo() where String: Copy {}` - // - // We don't usually need to worry about this kind of case, - // since we would get a compilation error if the user tried - // to call it. However, since we optimize even without any - // calls to the function, we need to make sure that it even - // makes sense to try to evaluate the body. - // - // If there are unsatisfiable where clauses, then all bets are - // off, and we just give up. - // - // We manually filter the predicates, skipping anything that's not - // "global". We are in a potentially generic context - // (e.g. we are evaluating a function without instantiating generic - // parameters, so this filtering serves two purposes: - // - // 1. We skip evaluating any predicates that we would - // never be able prove are unsatisfiable (e.g. `` - // 2. We avoid trying to normalize predicates involving generic - // parameters (e.g. `::MyItem`). This can confuse - // the normalization code (leading to cycle errors), since - // it's usually never invoked in this way. - let predicates = tcx - .predicates_of(body.source.def_id()) - .predicates - .iter() - .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); - if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) { - trace!("found unsatisfiable predicates for {:?}", body.source); - // Clear the body to only contain a single `unreachable` statement. - let bbs = body.basic_blocks.as_mut(); - bbs.raw.truncate(1); - bbs[START_BLOCK].statements.clear(); - bbs[START_BLOCK].terminator_mut().kind = TerminatorKind::Unreachable; - body.var_debug_info.clear(); - body.local_decls.raw.truncate(body.arg_count + 1); - } - run_analysis_to_runtime_passes(tcx, &mut body); // Now that drop elaboration has been performed, we can check for @@ -591,6 +549,7 @@ pub fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' /// After this series of passes, no lifetime analysis based on borrowing can be done. fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let passes: &[&dyn MirPass<'tcx>] = &[ + &impossible_predicates::ImpossiblePredicates, &cleanup_post_borrowck::CleanupPostBorrowck, &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::PostAnalysis, @@ -664,6 +623,8 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // Perform instsimplify before inline to eliminate some trivial calls (like clone // shims). &instsimplify::InstSimplify::BeforeInline, + // Perform inlining of `#[rustc_force_inline]`-annotated callees. + &inline::ForceInline, // Perform inlining, which may add a lot of code. &inline::Inline, // Code from other crates may have storage markers, so this needs to happen after diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index 29e762af8de3..f472c7cb493d 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -5,7 +5,7 @@ use std::borrow::Cow; use rustc_data_structures::fx::FxHashSet; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -43,7 +43,7 @@ struct Lint<'a, 'tcx> { when: String, body: &'a Body<'tcx>, is_fn_like: bool, - always_live_locals: &'a BitSet, + always_live_locals: &'a DenseBitSet, maybe_storage_live: ResultsCursor<'a, 'tcx, MaybeStorageLive<'a>>, maybe_storage_dead: ResultsCursor<'a, 'tcx, MaybeStorageDead<'a>>, places: FxHashSet>, 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..50d10883d2ca 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() { @@ -686,7 +691,7 @@ impl Subdiagnostic for LocalLabel<'_> { for dtor in self.destructors { dtor.add_to_diag_with(diag, f); } - let msg = f(diag, crate::fluent_generated::mir_transform_label_local_epilogue.into()); + let msg = f(diag, crate::fluent_generated::mir_transform_label_local_epilogue); diag.span_label(self.span, msg); } } diff --git a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs index a9227524ce5e..6dfa14d6b527 100644 --- a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs +++ b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs @@ -1,7 +1,7 @@ //! This pass removes jumps to basic blocks containing only a return, and replaces them with a //! return instead. -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -16,7 +16,7 @@ impl<'tcx> crate::MirPass<'tcx> for MultipleReturnTerminators { fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // find basic blocks with no statement and a return terminator - let mut bbs_simple_returns = BitSet::new_empty(body.basic_blocks.len()); + let mut bbs_simple_returns = DenseBitSet::new_empty(body.basic_blocks.len()); let bbs = body.basic_blocks_mut(); for idx in bbs.indices() { if bbs[idx].statements.is_empty() diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs index cd026ed68069..35872de38524 100644 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ b/compiler/rustc_mir_transform/src/nrvo.rs @@ -1,7 +1,7 @@ //! See the docs for [`RenameReturnPlace`]. use rustc_hir::Mutability; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, BasicBlock, Local, Location}; @@ -116,7 +116,7 @@ fn local_eligible_for_nrvo(body: &mir::Body<'_>) -> Option { fn find_local_assigned_to_return_place(start: BasicBlock, body: &mir::Body<'_>) -> Option { let mut block = start; - let mut seen = BitSet::new_empty(body.basic_blocks.len()); + let mut seen = DenseBitSet::new_empty(body.basic_blocks.len()); // Iterate as long as `block` has exactly one predecessor that we have not yet visited. while seen.insert(block) { diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 8a45ce0762d5..c3f0a989ce10 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -79,6 +79,12 @@ pub(super) trait MirPass<'tcx> { true } + /// Returns `true` if this pass can be overridden by `-Zenable-mir-passes`. This should be + /// true for basically every pass other than those that are necessary for correctness. + fn can_be_overridden(&self) -> bool { + true + } + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>); fn is_mir_dump_enabled(&self) -> bool { @@ -176,6 +182,10 @@ where { let name = pass.name(); + if !pass.can_be_overridden() { + return pass.is_enabled(tcx.sess); + } + let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes; let overridden = overridden_passes.iter().rev().find(|(s, _)| s == &*name).map(|(_name, polarity)| { diff --git a/compiler/rustc_mir_transform/src/prettify.rs b/compiler/rustc_mir_transform/src/prettify.rs index 937c207776b3..51abd4da86eb 100644 --- a/compiler/rustc_mir_transform/src/prettify.rs +++ b/compiler/rustc_mir_transform/src/prettify.rs @@ -4,7 +4,7 @@ //! (`-Zmir-enable-passes=+ReorderBasicBlocks,+ReorderLocals`) //! to make the MIR easier to read for humans. -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -51,8 +51,10 @@ impl<'tcx> crate::MirPass<'tcx> for ReorderLocals { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let mut finder = - LocalFinder { map: IndexVec::new(), seen: BitSet::new_empty(body.local_decls.len()) }; + let mut finder = LocalFinder { + map: IndexVec::new(), + seen: DenseBitSet::new_empty(body.local_decls.len()), + }; // We can't reorder the return place or the arguments for local in (0..=body.arg_count).map(Local::from_usize) { @@ -113,7 +115,7 @@ impl<'tcx> MutVisitor<'tcx> for BasicBlockUpdater<'tcx> { struct LocalFinder { map: IndexVec, - seen: BitSet, + seen: DenseBitSet, } impl LocalFinder { diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index 96bcdfa6fac4..95b05f94270a 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; @@ -132,7 +132,7 @@ fn compute_replacement<'tcx>( let mut targets = IndexVec::from_elem(Value::Unknown, &body.local_decls); // Set of locals for which we will remove their storage statement. This is useful for // reborrowed references. - let mut storage_to_remove = BitSet::new_empty(body.local_decls.len()); + let mut storage_to_remove = DenseBitSet::new_empty(body.local_decls.len()); let fully_replacable_locals = fully_replacable_locals(ssa); @@ -324,8 +324,8 @@ fn compute_replacement<'tcx>( /// /// We consider a local to be replacable iff it's only used in a `Deref` projection `*_local` or /// non-use position (like storage statements and debuginfo). -fn fully_replacable_locals(ssa: &SsaLocals) -> BitSet { - let mut replacable = BitSet::new_empty(ssa.num_locals()); +fn fully_replacable_locals(ssa: &SsaLocals) -> DenseBitSet { + let mut replacable = DenseBitSet::new_empty(ssa.num_locals()); // First pass: for each local, whether its uses can be fully replaced. for local in ssa.locals() { @@ -344,7 +344,7 @@ fn fully_replacable_locals(ssa: &SsaLocals) -> BitSet { struct Replacer<'tcx> { tcx: TyCtxt<'tcx>, targets: IndexVec>, - storage_to_remove: BitSet, + storage_to_remove: DenseBitSet, allowed_replacements: FxHashSet<(Local, Location)>, any_replacement: bool, } diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index fd49e956f433..76a3edfe0be4 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -1,4 +1,4 @@ -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -40,7 +40,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { let mut jumps_folded = 0; let mut landing_pads_removed = 0; - let mut nop_landing_pads = BitSet::new_empty(body.basic_blocks.len()); + let mut nop_landing_pads = DenseBitSet::new_empty(body.basic_blocks.len()); // This is a post-order traversal, so that if A post-dominates B // then A will be visited before B. @@ -81,7 +81,7 @@ impl RemoveNoopLandingPads { &self, bb: BasicBlock, body: &Body<'_>, - nop_landing_pads: &BitSet, + nop_landing_pads: &DenseBitSet, ) -> bool { for stmt in &body[bb].statements { match &stmt.kind { diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 64e183bcbc01..55e5701bd0af 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -36,31 +36,39 @@ struct Replacer<'a, 'tcx> { } /// A cheap, approximate check to avoid unnecessary `layout_of` calls. -fn maybe_zst(ty: Ty<'_>) -> bool { +/// +/// `Some(true)` is definitely ZST; `Some(false)` is definitely *not* ZST. +/// +/// `None` may or may not be, and must check `layout_of` to be sure. +fn trivially_zst<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option { match ty.kind() { - // maybe ZST (could be more precise) - ty::Adt(..) - | ty::Array(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Tuple(..) - | ty::Alias(ty::Opaque, ..) => true, // definitely ZST - ty::FnDef(..) | ty::Never => true, - // unreachable or can't be ZST - _ => false, + ty::FnDef(..) | ty::Never => Some(true), + ty::Tuple(fields) if fields.is_empty() => Some(true), + ty::Array(_ty, len) if let Some(0) = len.try_to_target_usize(tcx) => Some(true), + // clearly not ZST + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnPtr(..) => Some(false), + // check `layout_of` to see (including unreachable things we won't actually see) + _ => None, } } impl<'tcx> Replacer<'_, 'tcx> { fn known_to_be_zst(&self, ty: Ty<'tcx>) -> bool { - if !maybe_zst(ty) { - return false; + if let Some(is_zst) = trivially_zst(ty, self.tcx) { + is_zst + } else { + self.tcx + .layout_of(self.typing_env.as_query_input(ty)) + .is_ok_and(|layout| layout.is_zst()) } - let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(ty)) else { - return false; - }; - layout.is_zst() } fn make_zst(&self, ty: Ty<'tcx>) -> ConstOperand<'tcx> { diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 722da3c420dc..4648ec33c934 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -20,7 +20,7 @@ use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; use crate::{ - abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, + abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, inline, instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify, }; @@ -155,6 +155,8 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::MakeShim, &instsimplify::InstSimplify::BeforeInline, + // Perform inlining of `#[rustc_force_inline]`-annotated callees. + &inline::ForceInline, &abort_unwinding_calls::AbortUnwindingCalls, &add_call_guards::CriticalCallEdges, ], diff --git a/compiler/rustc_mir_transform/src/single_use_consts.rs b/compiler/rustc_mir_transform/src/single_use_consts.rs index 277a33c03115..10b3c0ae94f2 100644 --- a/compiler/rustc_mir_transform/src/single_use_consts.rs +++ b/compiler/rustc_mir_transform/src/single_use_consts.rs @@ -1,5 +1,5 @@ use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -28,9 +28,9 @@ impl<'tcx> crate::MirPass<'tcx> for SingleUseConsts { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let mut finder = SingleUseConstsFinder { - ineligible_locals: BitSet::new_empty(body.local_decls.len()), + ineligible_locals: DenseBitSet::new_empty(body.local_decls.len()), locations: IndexVec::from_elem(LocationPair::new(), &body.local_decls), - locals_in_debug_info: BitSet::new_empty(body.local_decls.len()), + locals_in_debug_info: DenseBitSet::new_empty(body.local_decls.len()), }; finder.ineligible_locals.insert_range(..=Local::from_usize(body.arg_count)); @@ -96,9 +96,9 @@ impl LocationPair { } struct SingleUseConstsFinder { - ineligible_locals: BitSet, + ineligible_locals: DenseBitSet, locations: IndexVec, - locals_in_debug_info: BitSet, + locals_in_debug_info: DenseBitSet, } impl<'tcx> Visitor<'tcx> for SingleUseConstsFinder { diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index 52b9ec1e0a35..d54ea3feab6e 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -2,7 +2,7 @@ use rustc_abi::{FIRST_VARIANT, FieldIdx}; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_hir::LangItem; use rustc_index::IndexVec; -use rustc_index::bit_set::{BitSet, GrowableBitSet}; +use rustc_index::bit_set::{DenseBitSet, GrowableBitSet}; use rustc_middle::bug; use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::visit::*; @@ -60,9 +60,9 @@ impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates { fn escaping_locals<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - excluded: &BitSet, + excluded: &DenseBitSet, body: &Body<'tcx>, -) -> BitSet { +) -> DenseBitSet { let is_excluded_ty = |ty: Ty<'tcx>| { if ty.is_union() || ty.is_enum() { return true; @@ -97,7 +97,7 @@ fn escaping_locals<'tcx>( false }; - let mut set = BitSet::new_empty(body.local_decls.len()); + let mut set = DenseBitSet::new_empty(body.local_decls.len()); set.insert_range(RETURN_PLACE..=Local::from_usize(body.arg_count)); for (local, decl) in body.local_decls().iter_enumerated() { if excluded.contains(local) || is_excluded_ty(decl.ty) { @@ -109,7 +109,7 @@ fn escaping_locals<'tcx>( return visitor.set; struct EscapeVisitor { - set: BitSet, + set: DenseBitSet, } impl<'tcx> Visitor<'tcx> for EscapeVisitor { @@ -198,7 +198,7 @@ fn compute_flattening<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, body: &mut Body<'tcx>, - escaping: BitSet, + escaping: DenseBitSet, ) -> ReplacementMap<'tcx> { let mut fragments = IndexVec::from_elem(None, &body.local_decls); @@ -226,8 +226,8 @@ fn replace_flattened_locals<'tcx>( tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, replacements: ReplacementMap<'tcx>, -) -> BitSet { - let mut all_dead_locals = BitSet::new_empty(replacements.fragments.len()); +) -> DenseBitSet { + let mut all_dead_locals = DenseBitSet::new_empty(replacements.fragments.len()); for (local, replacements) in replacements.fragments.iter_enumerated() { if replacements.is_some() { all_dead_locals.insert(local); @@ -267,7 +267,7 @@ struct ReplacementVisitor<'tcx, 'll> { /// Work to do. replacements: &'ll ReplacementMap<'tcx>, /// This is used to check that we are not leaving references to replaced locals behind. - all_dead_locals: BitSet, + all_dead_locals: DenseBitSet, patch: MirPatch<'tcx>, } diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index 5653aef0aae0..a24b3b2e80ff 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -7,7 +7,7 @@ //! of a `Freeze` local. Those can still be considered to be SSA. use rustc_data_structures::graph::dominators::Dominators; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::bug; use rustc_middle::middle::resolve_bound_vars::Set1; @@ -29,7 +29,7 @@ pub(super) struct SsaLocals { /// We ignore non-uses (Storage statements, debuginfo). direct_uses: IndexVec, /// Set of SSA locals that are immutably borrowed. - borrowed_locals: BitSet, + borrowed_locals: DenseBitSet, } pub(super) enum AssignedValue<'a, 'tcx> { @@ -50,7 +50,7 @@ impl SsaLocals { let dominators = body.basic_blocks.dominators(); let direct_uses = IndexVec::from_elem(0, &body.local_decls); - let borrowed_locals = BitSet::new_empty(body.local_decls.len()); + let borrowed_locals = DenseBitSet::new_empty(body.local_decls.len()); let mut visitor = SsaVisitor { body, assignments, @@ -202,12 +202,12 @@ impl SsaLocals { } /// Set of SSA locals that are immutably borrowed. - pub(super) fn borrowed_locals(&self) -> &BitSet { + pub(super) fn borrowed_locals(&self) -> &DenseBitSet { &self.borrowed_locals } /// Make a property uniform on a copy equivalence class by removing elements. - pub(super) fn meet_copy_equivalence(&self, property: &mut BitSet) { + pub(super) fn meet_copy_equivalence(&self, property: &mut DenseBitSet) { // Consolidate to have a local iff all its copies are. // // `copy_classes` defines equivalence classes between locals. The `local`s that recursively @@ -241,7 +241,7 @@ struct SsaVisitor<'a, 'tcx> { assignment_order: Vec, direct_uses: IndexVec, // Track locals that are immutably borrowed, so we can check their type is `Freeze` later. - borrowed_locals: BitSet, + borrowed_locals: DenseBitSet, } impl SsaVisitor<'_, '_> { @@ -396,7 +396,7 @@ pub(crate) struct StorageLiveLocals { impl StorageLiveLocals { pub(crate) fn new( body: &Body<'_>, - always_storage_live_locals: &BitSet, + always_storage_live_locals: &DenseBitSet, ) -> StorageLiveLocals { let mut storage_live = IndexVec::from_elem(Set1::Empty, &body.local_decls); for local in always_storage_live_locals.iter() { diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index a670da94fcc9..414477d90040 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1,10 +1,11 @@ //! Validates the MIR to ensure that invariants are upheld. use rustc_abi::{ExternAbi, FIRST_VARIANT, Size}; +use rustc_attr_parsing::InlineAttr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::LangItem; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_middle::mir::coverage::CoverageKind; @@ -97,7 +98,7 @@ struct CfgChecker<'a, 'tcx> { body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, unwind_edge_count: usize, - reachable_blocks: BitSet, + reachable_blocks: DenseBitSet, value_cache: FxHashSet, // If `false`, then the MIR must not contain `UnwindAction::Continue` or // `TerminatorKind::Resume`. @@ -366,7 +367,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { self.check_edge(location, *target, EdgeKind::Normal); self.check_unwind_edge(location, *unwind); } - TerminatorKind::Call { args, .. } | TerminatorKind::TailCall { args, .. } => { + TerminatorKind::Call { func, args, .. } + | TerminatorKind::TailCall { func, args, .. } => { // FIXME(explicit_tail_calls): refactor this & add tail-call specific checks if let TerminatorKind::Call { target, unwind, destination, .. } = terminator.kind { if let Some(target) = target { @@ -419,6 +421,13 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { } } } + + if let ty::FnDef(did, ..) = func.ty(&self.body.local_decls, self.tcx).kind() + && self.body.phase >= MirPhase::Runtime(RuntimePhase::Optimized) + && matches!(self.tcx.codegen_fn_attrs(did).inline, InlineAttr::Force { .. }) + { + self.fail(location, "`#[rustc_force_inline]`-annotated function not inlined"); + } } TerminatorKind::Assert { target, unwind, .. } => { self.check_edge(location, *target, EdgeKind::Normal); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 00aae03704fb..c2e9498908cd 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -207,6 +207,7 @@ use std::path::PathBuf; +use rustc_attr_parsing::InlineAttr; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{LRef, MTLock, par_for_each_in}; use rustc_data_structures::unord::{UnordMap, UnordSet}; @@ -224,8 +225,8 @@ use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::print::{shrunk_instance_name, with_no_trimmed_paths}; use rustc_middle::ty::{ - self, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, TypeFoldable, - TypeVisitableExt, VtblEntry, + self, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Interner, Ty, TyCtxt, + TypeFoldable, TypeVisitableExt, VtblEntry, }; use rustc_middle::util::Providers; use rustc_middle::{bug, span_bug}; @@ -959,6 +960,14 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxtAt<'tcx>, instance: Instance<'tcx>) - return false; } + if tcx.def_kind(def_id).has_codegen_attrs() + && matches!(tcx.codegen_fn_attrs(def_id).inline, InlineAttr::Force { .. }) + { + // `#[rustc_force_inline]` items should never be codegened. This should be caught by + // the MIR validator. + tcx.delay_bug("attempt to codegen `#[rustc_force_inline]` item"); + } + if def_id.is_local() { // Local items cannot be referred to locally without monomorphizing them locally. return true; @@ -1368,6 +1377,10 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionStrategy) -> Vec RootCollector<'_, 'v> { } } + fn process_nested_body(&mut self, def_id: LocalDefId) { + match self.tcx.def_kind(def_id) { + DefKind::Closure => { + if self.strategy == MonoItemCollectionStrategy::Eager + && !self + .tcx + .generics_of(self.tcx.typeck_root_def_id(def_id.to_def_id())) + .requires_monomorphization(self.tcx) + { + let instance = match *self.tcx.type_of(def_id).instantiate_identity().kind() { + ty::Closure(def_id, args) + | ty::Coroutine(def_id, args) + | ty::CoroutineClosure(def_id, args) => { + Instance::new(def_id, self.tcx.erase_regions(args)) + } + _ => unreachable!(), + }; + let mono_item = create_fn_mono_item(self.tcx, instance, DUMMY_SP); + if mono_item.node.is_instantiable(self.tcx) { + self.output.push(mono_item); + } + } + } + _ => {} + } + } + fn is_root(&self, def_id: LocalDefId) -> bool { !self.tcx.generics_of(def_id).requires_monomorphization(self.tcx) && match self.strategy { - MonoItemCollectionStrategy::Eager => true, + MonoItemCollectionStrategy::Eager => { + !matches!(self.tcx.codegen_fn_attrs(def_id).inline, InlineAttr::Force { .. }) + } MonoItemCollectionStrategy::Lazy => { self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id) || self.tcx.is_reachable_non_generic(def_id) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 7da4f5e01075..3c5d9b95e772 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -712,6 +712,8 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable( } } +// NOTE: Keep this in sync with `evaluate_host_effect_for_destruct_goal` in +// the old solver, for as long as that exists. pub(in crate::solve) fn const_conditions_for_destruct( cx: I, self_ty: I::Ty, 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_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 5ad3da2196f8..64bcb1a5a36c 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -656,14 +656,14 @@ impl<'a> Parser<'a> { fn visit_pat(&mut self, p: &'a Pat) -> Self::Result { match &p.kind { // Base expression - PatKind::Err(_) | PatKind::Lit(_) => { + PatKind::Err(_) | PatKind::Expr(_) => { self.maybe_add_suggestions_then_emit(p.span, p.span, false) } // Sub-patterns // FIXME: this doesn't work with recursive subpats (`&mut &mut `) PatKind::Box(subpat) | PatKind::Ref(subpat, _) - if matches!(subpat.kind, PatKind::Err(_) | PatKind::Lit(_)) => + if matches!(subpat.kind, PatKind::Err(_) | PatKind::Expr(_)) => { self.maybe_add_suggestions_then_emit(subpat.span, p.span, false) } @@ -766,7 +766,7 @@ impl<'a> Parser<'a> { if let Some(re) = self.parse_range_end() { self.parse_pat_range_begin_with(const_expr, re)? } else { - PatKind::Lit(const_expr) + PatKind::Expr(const_expr) } } else if self.is_builtin() { self.parse_pat_builtin()? @@ -833,7 +833,7 @@ impl<'a> Parser<'a> { .struct_span_err(self_.token.span, msg) .with_span_label(self_.token.span, format!("expected {expected}")) }); - PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit))) + PatKind::Expr(self.mk_expr(lo, ExprKind::Lit(lit))) } else { // Try to parse everything else as literal with optional minus match self.parse_literal_maybe_minus() { @@ -845,7 +845,7 @@ impl<'a> Parser<'a> { match self.parse_range_end() { Some(form) => self.parse_pat_range_begin_with(begin, form)?, - None => PatKind::Lit(begin), + None => PatKind::Expr(begin), } } Err(err) => return self.fatal_unexpected_non_pat(err, expected), @@ -989,7 +989,7 @@ impl<'a> Parser<'a> { match &pat.kind { // recover ranges with parentheses around the `(start)..` - PatKind::Lit(begin) + PatKind::Expr(begin) if self.may_recover() && let Some(form) = self.parse_range_end() => { diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 3f8d66c2c958..655ab8223597 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] +#![allow(rustc::symbol_intern_string_literal)] use std::assert_matches::assert_matches; use std::io::prelude::*; @@ -2366,8 +2366,7 @@ fn string_to_tts_1() { token::Ident(sym::i32, IdentIsRaw::No), sp(8, 11), ), - ]) - .into(), + ]), ), TokenTree::Delimited( DelimSpan::from_pair(sp(13, 14), sp(18, 19)), @@ -2383,8 +2382,7 @@ fn string_to_tts_1() { ), // `Alone` because the `;` is followed by whitespace. TokenTree::token_alone(token::Semi, sp(16, 17)), - ]) - .into(), + ]), ), ]); diff --git a/compiler/rustc_parse/src/parser/tokenstream/tests.rs b/compiler/rustc_parse/src/parser/tokenstream/tests.rs index 037b5b1a9de5..aac75323ff3d 100644 --- a/compiler/rustc_parse/src/parser/tokenstream/tests.rs +++ b/compiler/rustc_parse/src/parser/tokenstream/tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] +#![allow(rustc::symbol_intern_string_literal)] use rustc_ast::token::{self, IdentIsRaw}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index f39bea2a56fc..9df396cbf7a8 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -656,6 +656,14 @@ passes_rustc_allow_const_fn_unstable = passes_rustc_dirty_clean = attribute requires -Z query-dep-graph to be enabled +passes_rustc_force_inline = + attribute should be applied to a function + .label = not a function definition + +passes_rustc_force_inline_coro = + attribute cannot be applied to a `async`, `gen` or `async gen` function + .label = `async`, `gen` or `async gen` function + passes_rustc_layout_scalar_valid_range_arg = expected exactly one integer literal argument diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 7656ca86c184..1b2b8ac5dd9e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -247,7 +247,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_coroutine(attr, target); } [sym::linkage, ..] => self.check_linkage(attr, span, target), - [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent( attr.span, span, attrs), + [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent(attr.span, span, attrs), [ // ok sym::allow @@ -332,6 +332,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_repr(attrs, span, target, item, hir_id); self.check_used(attrs, target, span); + self.check_rustc_force_inline(hir_id, attrs, span, target); } fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { @@ -2480,6 +2481,45 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + fn check_rustc_force_inline( + &self, + hir_id: HirId, + attrs: &[Attribute], + span: Span, + target: Target, + ) { + let force_inline_attr = attrs.iter().find(|attr| attr.has_name(sym::rustc_force_inline)); + match (target, force_inline_attr) { + (Target::Closure, None) => { + let is_coro = matches!( + self.tcx.hir().expect_expr(hir_id).kind, + hir::ExprKind::Closure(hir::Closure { + kind: hir::ClosureKind::Coroutine(..) + | hir::ClosureKind::CoroutineClosure(..), + .. + }) + ); + let parent_did = self.tcx.hir().get_parent_item(hir_id).to_def_id(); + let parent_span = self.tcx.def_span(parent_did); + let parent_force_inline_attr = + self.tcx.get_attr(parent_did, sym::rustc_force_inline); + if let Some(attr) = parent_force_inline_attr + && is_coro + { + self.dcx().emit_err(errors::RustcForceInlineCoro { + attr_span: attr.span, + span: parent_span, + }); + } + } + (Target::Fn, _) => (), + (_, Some(attr)) => { + self.dcx().emit_err(errors::RustcForceInline { attr_span: attr.span, span }); + } + (_, None) => (), + } + } + /// Checks if `#[autodiff]` is applied to an item other than a function item. fn check_autodiff(&self, _hir_id: HirId, _attr: &Attribute, span: Span, target: Target) { debug!("check_autodiff"); diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index d95fa5db0cee..13da021c6143 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -688,6 +688,24 @@ pub(crate) struct RustcPubTransparent { pub span: Span, } +#[derive(Diagnostic)] +#[diag(passes_rustc_force_inline)] +pub(crate) struct RustcForceInline { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_rustc_force_inline_coro)] +pub(crate) struct RustcForceInlineCoro { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(passes_link_ordinal)] pub(crate) struct LinkOrdinal { diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index f9cb8c9b9271..6617cf2f723a 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -304,7 +304,8 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { Box, Deref, Ref, - Lit, + Expr, + Guard, Range, Slice, Err @@ -586,7 +587,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Box, Deref, Ref, - Lit, + Expr, Range, Slice, Rest, 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_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 8fce42663453..4ce868f014f4 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -182,7 +182,7 @@ use std::iter::once; use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, SingleS}; use rustc_index::IndexVec; -use rustc_index::bit_set::{BitSet, GrowableBitSet}; +use rustc_index::bit_set::{DenseBitSet, GrowableBitSet}; use smallvec::SmallVec; use self::Constructor::*; @@ -1072,7 +1072,7 @@ impl ConstructorSet { } } ConstructorSet::Variants { variants, non_exhaustive } => { - let mut seen_set = BitSet::new_empty(variants.len()); + let mut seen_set = DenseBitSet::new_empty(variants.len()); for idx in seen.iter().filter_map(|c| c.as_variant()) { seen_set.insert(idx); } diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 99261eaa95cc..cc09cd491af1 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -712,7 +712,7 @@ use std::fmt; #[cfg(feature = "rustc")] use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -1129,7 +1129,7 @@ struct MatrixRow<'p, Cx: PatCx> { /// ``` /// Here the `(true, true)` case is irrelevant. Since we skip it, we will not detect that row 0 /// intersects rows 1 and 2. - intersects_at_least: BitSet, + intersects_at_least: DenseBitSet, /// Whether the head pattern is a branch (see definition of "branch pattern" at /// [`BranchPatUsefulness`]) head_is_branch: bool, @@ -1142,7 +1142,7 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> { parent_row: arm_id, is_under_guard: arm.has_guard, useful: false, - intersects_at_least: BitSet::new_empty(0), // Initialized in `Matrix::push`. + intersects_at_least: DenseBitSet::new_empty(0), // Initialized in `Matrix::push`. // This pattern is a branch because it comes from a match arm. head_is_branch: true, } @@ -1171,7 +1171,7 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> { parent_row, is_under_guard: self.is_under_guard, useful: false, - intersects_at_least: BitSet::new_empty(0), // Initialized in `Matrix::push`. + intersects_at_least: DenseBitSet::new_empty(0), // Initialized in `Matrix::push`. head_is_branch: is_or_pat, }) } @@ -1191,7 +1191,7 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> { parent_row, is_under_guard: self.is_under_guard, useful: false, - intersects_at_least: BitSet::new_empty(0), // Initialized in `Matrix::push`. + intersects_at_least: DenseBitSet::new_empty(0), // Initialized in `Matrix::push`. head_is_branch: false, }) } @@ -1230,7 +1230,7 @@ struct Matrix<'p, Cx: PatCx> { impl<'p, Cx: PatCx> Matrix<'p, Cx> { /// Pushes a new row to the matrix. Internal method, prefer [`Matrix::new`]. fn push(&mut self, mut row: MatrixRow<'p, Cx>) { - row.intersects_at_least = BitSet::new_empty(self.rows.len()); + row.intersects_at_least = DenseBitSet::new_empty(self.rows.len()); self.rows.push(row); } @@ -1824,7 +1824,7 @@ pub struct UsefulnessReport<'p, Cx: PatCx> { pub non_exhaustiveness_witnesses: Vec>, /// For each arm, a set of indices of arms above it that have non-empty intersection, i.e. there /// is a value matched by both arms. This may miss real intersections. - pub arm_intersections: Vec>, + pub arm_intersections: Vec>, } /// Computes whether a match is exhaustive and which of its arms are useful. diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 6ee02e9f47f1..2936d722aa7f 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1130,7 +1130,9 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let None = following_seg else { return }; for rib in self.ribs[ValueNS].iter().rev() { for (def_id, spans) in &rib.patterns_with_skipped_bindings { - if let Some(fields) = self.r.field_idents(*def_id) { + if let DefKind::Struct | DefKind::Variant = self.r.tcx.def_kind(*def_id) + && let Some(fields) = self.r.field_idents(*def_id) + { for field in fields { if field.name == segment.ident.name { if spans.iter().all(|(_, had_error)| had_error.is_err()) { @@ -3155,7 +3157,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } - #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + #[allow(rustc::symbol_intern_string_literal)] let existing_name = match &in_scope_lifetimes[..] { [] => Symbol::intern("'a"), [(existing, _)] => existing.name, 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.rs b/compiler/rustc_session/src/config.rs index 5c36c9864902..5192ad61af2b 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2892,6 +2892,7 @@ pub(crate) mod dep_tracking { use std::num::NonZero; use std::path::PathBuf; + use rustc_abi::Align; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hasher::Hash64; use rustc_errors::LanguageIdentifier; @@ -3012,6 +3013,7 @@ pub(crate) mod dep_tracking { InliningThreshold, FunctionReturn, WasmCAbi, + Align, ); impl DepTrackingHash for (T1, T2) 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..3af6df630177 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -4,6 +4,7 @@ use std::num::{IntErrorKind, NonZero}; use std::path::PathBuf; use std::str; +use rustc_abi::Align; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::profiling::TimePassesFormat; use rustc_data_structures::stable_hasher::Hash64; @@ -239,7 +240,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 +282,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 +318,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 +356,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( @@ -455,6 +483,7 @@ mod desc { pub(crate) const parse_wasm_c_abi: &str = "`legacy` or `spec`"; pub(crate) const parse_mir_include_spans: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or `nll` (default: `nll`)"; + pub(crate) const parse_align: &str = "a number that is a power of 2 between 1 and 2^29"; } pub mod parse { @@ -1534,6 +1563,21 @@ pub mod parse { true } + + pub(crate) fn parse_align(slot: &mut Option, v: Option<&str>) -> bool { + let mut bytes = 0u64; + if !parse_number(&mut bytes, v) { + return false; + } + + let Ok(align) = Align::from_bytes(bytes) else { + return false; + }; + + *slot = Some(align); + + true + } } options! { @@ -1546,7 +1590,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 +1623,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 +1662,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 +1818,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], @@ -1889,6 +1938,8 @@ options! { "gather metadata statistics (default: no)"), metrics_dir: Option = (None, parse_opt_pathbuf, [UNTRACKED], "the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"), + min_function_alignment: Option = (None, parse_align, [TRACKED], + "align all functions to at least this many bytes. Must be a power of 2"), mir_emit_retag: bool = (false, parse_bool, [TRACKED], "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ (default: no)"), diff --git a/compiler/rustc_span/src/edit_distance/tests.rs b/compiler/rustc_span/src/edit_distance/tests.rs index 9540f934d7eb..8372856c0d30 100644 --- a/compiler/rustc_span/src/edit_distance/tests.rs +++ b/compiler/rustc_span/src/edit_distance/tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] +#![allow(rustc::symbol_intern_string_literal)] use super::*; diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index d5c2a337b4c1..51cfbf594716 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -67,7 +67,7 @@ mod span_encoding; pub use span_encoding::{DUMMY_SP, Span}; pub mod symbol; -pub use symbol::{Ident, MacroRulesNormalizedIdent, Symbol, kw, sym}; +pub use symbol::{Ident, MacroRulesNormalizedIdent, STDLIB_STABLE_CRATES, Symbol, kw, sym}; mod analyze_source_file; pub mod fatal_error; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index dc76412c48ed..0c8c722e4d82 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, @@ -1730,6 +1732,7 @@ symbols! { rustc_error, rustc_evaluate_where_clauses, rustc_expected_cgu_reuse, + rustc_force_inline, rustc_has_incoherent_inherent_impls, rustc_hidden_type_of_opaques, rustc_if_this_changed, @@ -2239,6 +2242,10 @@ symbols! { } } +/// Symbols for crates that are part of the stable standard library: `std`, `core`, `alloc`, and +/// `proc_macro`. +pub const STDLIB_STABLE_CRATES: &[Symbol] = &[sym::std, sym::core, sym::alloc, sym::proc_macro]; + #[derive(Copy, Clone, Eq, HashStable_Generic, Encodable, Decodable)] pub struct Ident { pub name: Symbol, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 0d6d8488a23c..58c0a05df1f2 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -383,14 +383,47 @@ 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 generics = self.tcx.generics_of(impl_def_id); + // We have two cases to worry about here: + // 1. We're printing a nested item inside of an impl item, like an inner + // function inside of a method. Due to the way that def path printing works, + // we'll render this something like `::method::inner_fn` + // but we have no substs for this impl since it's not really inheriting + // generics from the outer item. We need to use the identity substs, and + // to normalize we need to use the correct param-env too. + // 2. We're mangling an item with identity substs. This seems to only happen + // when generating coverage, since we try to generate coverage for unused + // items too, and if something isn't monomorphized then we necessarily don't + // have anything to substitute the instance with. + // NOTE: We don't support mangling partially substituted but still polymorphic + // instances, like `impl Tr for ()` where `A` is substituted w/ `(T,)`. + let (typing_env, mut self_ty, mut impl_trait_ref) = if generics.count() > args.len() + || &args[..generics.count()] + == self + .tcx + .erase_regions(ty::GenericArgs::identity_for_item(self.tcx, impl_def_id)) + .as_slice() + { + ( + 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()), + ) + } else { + assert!( + !args.has_non_region_param(), + "should not be mangling partially substituted \ + polymorphic instance: {impl_def_id:?} {args:?}" + ); + ( + ty::TypingEnv::fully_monomorphized(), + self_ty.instantiate(self.tcx, args), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate(self.tcx, args)), + ) + }; match &mut impl_trait_ref { Some(impl_trait_ref) => { @@ -403,7 +436,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..4ddf530a00d4 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,50 @@ 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 generics = self.tcx.generics_of(impl_def_id); + // We have two cases to worry about here: + // 1. We're printing a nested item inside of an impl item, like an inner + // function inside of a method. Due to the way that def path printing works, + // we'll render this something like `::method::inner_fn` + // but we have no substs for this impl since it's not really inheriting + // generics from the outer item. We need to use the identity substs, and + // to normalize we need to use the correct param-env too. + // 2. We're mangling an item with identity substs. This seems to only happen + // when generating coverage, since we try to generate coverage for unused + // items too, and if something isn't monomorphized then we necessarily don't + // have anything to substitute the instance with. + // NOTE: We don't support mangling partially substituted but still polymorphic + // instances, like `impl Tr for ()` where `A` is substituted w/ `(T,)`. + let (typing_env, mut self_ty, mut impl_trait_ref) = if generics.count() > args.len() + || &args[..generics.count()] + == self + .tcx + .erase_regions(ty::GenericArgs::identity_for_item(self.tcx, impl_def_id)) + .as_slice() + { + ( + 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()), + ) + } else { + assert!( + !args.has_non_region_param(), + "should not be mangling partially substituted \ + polymorphic instance: {impl_def_id:?} {args:?}" + ); + ( + ty::TypingEnv::fully_monomorphized(), + self_ty.instantiate(self.tcx, args), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate(self.tcx, args)), + ) + }; 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 02962d55a60e..a149f682c560 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), @@ -1811,9 +1812,11 @@ supported_targets! { ("aarch64-unknown-illumos", aarch64_unknown_illumos), ("x86_64-pc-windows-gnu", x86_64_pc_windows_gnu), + ("x86_64-uwp-windows-gnu", x86_64_uwp_windows_gnu), + ("x86_64-win7-windows-gnu", x86_64_win7_windows_gnu), ("i686-pc-windows-gnu", i686_pc_windows_gnu), ("i686-uwp-windows-gnu", i686_uwp_windows_gnu), - ("x86_64-uwp-windows-gnu", x86_64_uwp_windows_gnu), + ("i686-win7-windows-gnu", i686_win7_windows_gnu), ("aarch64-pc-windows-gnullvm", aarch64_pc_windows_gnullvm), ("i686-pc-windows-gnullvm", i686_pc_windows_gnullvm), @@ -1926,6 +1929,8 @@ supported_targets! { ("mipsel-sony-psp", mipsel_sony_psp), ("mipsel-sony-psx", mipsel_sony_psx), ("mipsel-unknown-none", mipsel_unknown_none), + ("mips-mti-none-elf", mips_mti_none_elf), + ("mipsel-mti-none-elf", mipsel_mti_none_elf), ("thumbv4t-none-eabi", thumbv4t_none_eabi), ("armv4t-none-eabi", armv4t_none_eabi), ("thumbv5te-none-eabi", thumbv5te_none_eabi), @@ -2651,10 +2656,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 { @@ -3201,7 +3202,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" => { @@ -3209,7 +3211,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" => { @@ -3243,6 +3246,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/i686_win7_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_win7_windows_gnu.rs new file mode 100644 index 000000000000..086a799a68c3 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/i686_win7_windows_gnu.rs @@ -0,0 +1,35 @@ +use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, Target, base}; + +pub(crate) fn target() -> Target { + let mut base = base::windows_gnu::opts(); + base.vendor = "win7".into(); + base.cpu = "pentium4".into(); + base.max_atomic_width = Some(64); + base.frame_pointer = FramePointer::Always; // Required for backtraces + base.linker = Some("i686-w64-mingw32-gcc".into()); + + // Mark all dynamic libraries and executables as compatible with the larger 4GiB address + // space available to x86 Windows binaries on x86_64. + base.add_pre_link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ + "-m", + "i386pe", + "--large-address-aware", + ]); + base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-Wl,--large-address-aware"]); + + Target { + llvm_target: "i686-pc-windows-gnu".into(), + metadata: crate::spec::TargetMetadata { + description: Some("32-bit MinGW (Windows 7+)".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32" + .into(), + arch: "x86".into(), + options: base, + } +} 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/spec/targets/mips_mti_none_elf.rs b/compiler/rustc_target/src/spec/targets/mips_mti_none_elf.rs new file mode 100644 index 000000000000..4637e31fb174 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/mips_mti_none_elf.rs @@ -0,0 +1,36 @@ +use crate::abi::Endian; +use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub(crate) fn target() -> Target { + Target { + data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(), + llvm_target: "mips".into(), + metadata: crate::spec::TargetMetadata { + description: Some("MIPS32r2 BE Baremetal Softfloat".into()), + tier: Some(3), + host_tools: Some(false), + std: None, // ? + }, + pointer_width: 32, + arch: "mips".into(), + + options: TargetOptions { + vendor: "mti".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + endian: Endian::Big, + cpu: "mips32r2".into(), + + max_atomic_width: Some(32), + + features: "+mips32r2,+soft-float,+noabicalls".into(), + executables: true, + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + singlethread: true, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/mipsel_mti_none_elf.rs b/compiler/rustc_target/src/spec/targets/mipsel_mti_none_elf.rs new file mode 100644 index 000000000000..a89c0fce88c6 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/mipsel_mti_none_elf.rs @@ -0,0 +1,36 @@ +use crate::abi::Endian; +use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub(crate) fn target() -> Target { + Target { + data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(), + llvm_target: "mipsel".into(), + metadata: crate::spec::TargetMetadata { + description: Some("MIPS32r2 LE Baremetal Softfloat".into()), + tier: Some(3), + host_tools: Some(false), + std: None, // ? + }, + pointer_width: 32, + arch: "mips".into(), + + options: TargetOptions { + vendor: "mti".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + endian: Endian::Little, + cpu: "mips32r2".into(), + + max_atomic_width: Some(32), + + features: "+mips32r2,+soft-float,+noabicalls".into(), + executables: true, + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + singlethread: true, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_gnu.rs new file mode 100644 index 000000000000..d40df5a3e7d6 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_gnu.rs @@ -0,0 +1,32 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, Target, base}; + +pub(crate) fn target() -> Target { + let mut base = base::windows_gnu::opts(); + base.vendor = "win7".into(); + base.cpu = "x86-64".into(); + base.plt_by_default = false; + // Use high-entropy 64 bit address space for ASLR + base.add_pre_link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ + "-m", + "i386pep", + "--high-entropy-va", + ]); + base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64", "-Wl,--high-entropy-va"]); + base.max_atomic_width = Some(64); + base.linker = Some("x86_64-w64-mingw32-gcc".into()); + + Target { + llvm_target: "x86_64-pc-windows-gnu".into(), + metadata: crate::spec::TargetMetadata { + description: Some("64-bit MinGW (Windows 7+)".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 64, + data_layout: + "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), + arch: "x86_64".into(), + options: base, + } +} 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/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 1ab89ecde7a4..b82bb27eb795 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -251,7 +251,9 @@ trait_selection_no_value_in_rustc_on_unimplemented = this attribute must have a trait_selection_nothing = {""} -trait_selection_oc_cant_coerce = cannot coerce intrinsics to function pointers +trait_selection_oc_cant_coerce_force_inline = + cannot coerce functions which must be inlined to function pointers +trait_selection_oc_cant_coerce_intrinsic = cannot coerce intrinsics to function pointers trait_selection_oc_closure_selfref = closure/coroutine type that references itself trait_selection_oc_const_compat = const not compatible with trait trait_selection_oc_fn_lang_correct_type = {$lang_item_name -> diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index d279590d45ae..53300c95fa70 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -2294,7 +2294,7 @@ impl<'tcx> ObligationCause<'tcx> { { FailureCode::Error0644 } - TypeError::IntrinsicCast => FailureCode::Error0308, + TypeError::IntrinsicCast | TypeError::ForceInlineCast => FailureCode::Error0308, _ => FailureCode::Error0308, }, } @@ -2360,8 +2360,11 @@ impl<'tcx> ObligationCause<'tcx> { { ObligationCauseFailureCode::ClosureSelfref { span } } + TypeError::ForceInlineCast => { + ObligationCauseFailureCode::CantCoerceForceInline { span, subdiags } + } TypeError::IntrinsicCast => { - ObligationCauseFailureCode::CantCoerce { span, subdiags } + ObligationCauseFailureCode::CantCoerceIntrinsic { span, subdiags } } _ => ObligationCauseFailureCode::Generic { span, subdiags }, }, 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..405c26b5b3b5 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 @@ -27,7 +27,7 @@ use rustc_middle::ty::{ self, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; use rustc_middle::{bug, span_bug}; -use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, sym}; +use rustc_span::{BytePos, DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym}; use tracing::{debug, instrument}; use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote}; @@ -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()); @@ -520,7 +520,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { match obligation.cause.span.ctxt().outer_expn_data().macro_def_id { Some(macro_def_id) => { let crate_name = tcx.crate_name(macro_def_id.krate); - crate_name == sym::std || crate_name == sym::core + STDLIB_STABLE_CRATES.contains(&crate_name) } None => false, }; @@ -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/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index a8fddff4e4a8..53a4e5031c6d 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1729,8 +1729,15 @@ pub enum ObligationCauseFailureCode { #[primary_span] span: Span, }, - #[diag(trait_selection_oc_cant_coerce, code = E0308)] - CantCoerce { + #[diag(trait_selection_oc_cant_coerce_force_inline, code = E0308)] + CantCoerceForceInline { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(trait_selection_oc_cant_coerce_intrinsic, code = E0308)] + CantCoerceIntrinsic { #[primary_span] span: Span, #[subdiagnostic] diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index ee708564a804..f373706b2960 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -47,6 +47,12 @@ impl<'tcx> InferCtxt<'tcx> { traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id) } + fn type_is_clone_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { + let ty = self.resolve_vars_if_possible(ty); + let clone_def_id = self.tcx.require_lang_item(LangItem::Clone, None); + traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, clone_def_id) + } + fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item) 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/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 15eb5d74cbfa..446f9eaa348c 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -79,7 +79,7 @@ pub fn is_const_evaluatable<'tcx>( Err( EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e), - ) => Err(NotConstEvaluatable::Error(e.into())), + ) => Err(NotConstEvaluatable::Error(e)), Ok(_) => Ok(()), } } @@ -140,7 +140,7 @@ pub fn is_const_evaluatable<'tcx>( } Err( EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e), - ) => Err(NotConstEvaluatable::Error(e.into())), + ) => Err(NotConstEvaluatable::Error(e)), Ok(_) => Ok(()), } } diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 91484ef99dbb..b32909efe0be 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_hir::{self as hir, LangItem}; 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}; @@ -46,6 +48,12 @@ pub fn evaluate_host_effect_obligation<'tcx>( Err(EvaluationFailure::NoSolution) => {} } + match evaluate_host_effect_from_builtin_impls(selcx, obligation) { + Ok(result) => return Ok(result), + Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), + Err(EvaluationFailure::NoSolution) => {} + } + match evaluate_host_effect_from_selection_candiate(selcx, obligation) { Ok(result) => return Ok(result), Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), @@ -226,6 +234,104 @@ fn evaluate_host_effect_from_item_bounds<'tcx>( } } +fn evaluate_host_effect_from_builtin_impls<'tcx>( + selcx: &mut SelectionContext<'_, 'tcx>, + obligation: &HostEffectObligation<'tcx>, +) -> Result>, EvaluationFailure> { + match selcx.tcx().as_lang_item(obligation.predicate.def_id()) { + Some(LangItem::Destruct) => evaluate_host_effect_for_destruct_goal(selcx, obligation), + _ => Err(EvaluationFailure::NoSolution), + } +} + +// NOTE: Keep this in sync with `const_conditions_for_destruct` in the new solver. +fn evaluate_host_effect_for_destruct_goal<'tcx>( + selcx: &mut SelectionContext<'_, 'tcx>, + obligation: &HostEffectObligation<'tcx>, +) -> Result>, EvaluationFailure> { + let tcx = selcx.tcx(); + let destruct_def_id = tcx.require_lang_item(LangItem::Destruct, None); + let self_ty = obligation.predicate.self_ty(); + + let const_conditions = match *self_ty.kind() { + // An ADT is `~const Destruct` only if all of the fields are, + // *and* if there is a `Drop` impl, that `Drop` impl is also `~const`. + ty::Adt(adt_def, args) => { + let mut const_conditions: ThinVec<_> = adt_def + .all_fields() + .map(|field| ty::TraitRef::new(tcx, destruct_def_id, [field.ty(tcx, args)])) + .collect(); + match adt_def.destructor(tcx).map(|dtor| dtor.constness) { + // `Drop` impl exists, but it's not const. Type cannot be `~const Destruct`. + Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution), + // `Drop` impl exists, and it's const. Require `Ty: ~const Drop` to hold. + Some(hir::Constness::Const) => { + let drop_def_id = tcx.require_lang_item(LangItem::Drop, None); + let drop_trait_ref = ty::TraitRef::new(tcx, drop_def_id, [self_ty]); + const_conditions.push(drop_trait_ref); + } + // No `Drop` impl, no need to require anything else. + None => {} + } + const_conditions + } + + ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => { + thin_vec![ty::TraitRef::new(tcx, destruct_def_id, [ty])] + } + + ty::Tuple(tys) => { + tys.iter().map(|field_ty| ty::TraitRef::new(tcx, destruct_def_id, [field_ty])).collect() + } + + // Trivially implement `~const Destruct` + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Str + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Never + | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) + | ty::Error(_) => thin_vec![], + + // Coroutines and closures could implement `~const Drop`, + // but they don't really need to right now. + ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(_, _) => return Err(EvaluationFailure::NoSolution), + + // FIXME(unsafe_binders): Unsafe binders could implement `~const Drop` + // if their inner type implements it. + ty::UnsafeBinder(_) => return Err(EvaluationFailure::NoSolution), + + ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => { + return Err(EvaluationFailure::NoSolution); + } + + ty::Bound(..) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + panic!("unexpected type `{self_ty:?}`") + } + }; + + Ok(const_conditions + .into_iter() + .map(|trait_ref| { + obligation.with( + tcx, + ty::Binder::dummy(trait_ref) + .to_host_effect_clause(tcx, obligation.predicate.constness), + ) + }) + .collect()) +} + fn evaluate_host_effect_from_selection_candiate<'tcx>( selcx: &mut SelectionContext<'_, 'tcx>, obligation: &HostEffectObligation<'tcx>, @@ -248,9 +354,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_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 637e239a5701..51a7c976f600 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -4,7 +4,7 @@ use rustc_abi::{FIRST_VARIANT, VariantIdx}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::query::Providers; use rustc_middle::thir::visit; use rustc_middle::thir::visit::Visitor; @@ -118,13 +118,7 @@ fn recurse_build<'tcx>( } &ExprKind::Literal { lit, neg } => { let sp = node.span; - match tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) { - Ok(c) => c, - Err(LitToConstError::Reported(guar)) => ty::Const::new_error(tcx, guar), - Err(LitToConstError::TypeError) => { - bug!("encountered type error in lit_to_const") - } - } + tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) } &ExprKind::NonHirLiteral { lit, user_ty: _ } => { let val = ty::ValTree::from_scalar_int(lit); diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 9f138cf1275d..ab606478c513 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -9,7 +9,7 @@ use rustc_abi::{ HasDataLayout, Layout, LayoutCalculatorError, LayoutData, Niche, ReprOptions, Scalar, Size, StructKind, TagEncoding, VariantIdx, Variants, WrappingRange, }; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::bug; use rustc_middle::mir::{CoroutineLayout, CoroutineSavedLocal}; @@ -347,6 +347,7 @@ fn layout_of_uncached<'tcx>( size, max_repr_align: None, unadjusted_abi_align: element.align.abi, + randomization_seed: element.randomization_seed.wrapping_add(count), }) } ty::Slice(element) => { @@ -360,6 +361,8 @@ fn layout_of_uncached<'tcx>( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: element.align.abi, + // adding a randomly chosen value to distinguish slices + randomization_seed: element.randomization_seed.wrapping_add(0x2dcba99c39784102), }) } ty::Str => tcx.mk_layout(LayoutData { @@ -371,6 +374,8 @@ fn layout_of_uncached<'tcx>( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: dl.i8_align.abi, + // another random value + randomization_seed: 0xc1325f37d127be22, }), // Odd unit types. @@ -542,6 +547,7 @@ fn layout_of_uncached<'tcx>( align, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed: e_ly.randomization_seed.wrapping_add(e_len), }) } @@ -718,7 +724,7 @@ enum SavedLocalEligibility { /// Compute the eligibility and assignment of each local. fn coroutine_saved_local_eligibility( info: &CoroutineLayout<'_>, -) -> (BitSet, IndexVec) { +) -> (DenseBitSet, IndexVec) { use SavedLocalEligibility::*; let mut assignments: IndexVec = @@ -726,7 +732,7 @@ fn coroutine_saved_local_eligibility( // The saved locals not eligible for overlap. These will get // "promoted" to the prefix of our coroutine. - let mut ineligible_locals = BitSet::new_empty(info.field_tys.len()); + let mut ineligible_locals = DenseBitSet::new_empty(info.field_tys.len()); // Figure out which of our saved locals are fields in only // one variant. The rest are deemed ineligible for overlap. @@ -786,7 +792,7 @@ fn coroutine_saved_local_eligibility( // lay them out with the other locals in the prefix and eliminate // unnecessary padding bytes. { - let mut used_variants = BitSet::new_empty(info.variant_fields.len()); + let mut used_variants = DenseBitSet::new_empty(info.variant_fields.len()); for assignment in &assignments { if let Assigned(idx) = assignment { used_variants.insert(*idx); @@ -999,6 +1005,9 @@ fn coroutine_layout<'tcx>( BackendRepr::Memory { sized: true } }; + // this is similar to how ReprOptions populates its field_shuffle_seed + let def_hash = tcx.def_path_hash(def_id).0.to_smaller_hash().as_u64(); + let layout = tcx.mk_layout(LayoutData { variants: Variants::Multiple { tag, @@ -1019,6 +1028,7 @@ fn coroutine_layout<'tcx>( align, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed: def_hash, }); debug!("coroutine layout ({:?}): {:#?}", ty, layout); Ok(layout) diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs index 0ffb7f624965..98b1550e1a3d 100644 --- a/compiler/rustc_ty_utils/src/representability.rs +++ b/compiler/rustc_ty_utils/src/representability.rs @@ -1,5 +1,5 @@ use rustc_hir::def::DefKind; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{self, Representability, Ty, TyCtxt}; @@ -83,10 +83,10 @@ fn representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representab Representability::Representable } -fn params_in_repr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> BitSet { +fn params_in_repr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> DenseBitSet { let adt_def = tcx.adt_def(def_id); let generics = tcx.generics_of(def_id); - let mut params_in_repr = BitSet::new_empty(generics.own_params.len()); + let mut params_in_repr = DenseBitSet::new_empty(generics.own_params.len()); for variant in adt_def.variants() { for field in variant.fields.iter() { params_in_repr_ty( @@ -99,7 +99,7 @@ fn params_in_repr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> BitSet { params_in_repr } -fn params_in_repr_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, params_in_repr: &mut BitSet) { +fn params_in_repr_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, params_in_repr: &mut DenseBitSet) { match *ty.kind() { ty::Adt(adt, args) => { let inner_params_in_repr = tcx.params_in_repr(adt.did()); diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 7eed32e3a336..8ed45b4e5415 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::LangItem; use rustc_hir::def::DefKind; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::fold::fold_regions; @@ -317,7 +317,7 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Asyncness { }) } -fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> BitSet { +fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> DenseBitSet { let def = tcx.adt_def(def_id); let num_params = tcx.generics_of(def_id).count(); @@ -338,10 +338,10 @@ fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> BitSet, ty::TraitRef, ty::ExistentialTraitRef, + ty::HostEffectPredicate, } impl Binder diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index 55671b84dbc4..68b11489ae7c 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -51,6 +51,9 @@ pub enum TypeError { ConstMismatch(ExpectedFound), IntrinsicCast, + /// `#[rustc_force_inline]` functions must be inlined and must not be codegened independently, + /// so casting to a function pointer must be prohibited. + ForceInlineCast, /// Safe `#[target_feature]` functions are not assignable to safe function pointers. TargetFeatureCast(I::DefId), } @@ -83,6 +86,7 @@ impl TypeError { | ProjectionMismatched(_) | ExistentialMismatch(_) | ConstMismatch(_) + | ForceInlineCast | IntrinsicCast => true, } } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 025ec7ae896d..4fec606a8315 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -3,7 +3,7 @@ use std::hash::Hash; use std::ops::Deref; use rustc_ast_ir::Movability; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use smallvec::SmallVec; use crate::fold::TypeFoldable; @@ -282,7 +282,7 @@ pub trait Interner: fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool; fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool; - type UnsizingParams: Deref>; + type UnsizingParams: Deref>; fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams; fn find_const_ty_from_env( 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/config.example.toml b/config.example.toml index 5ea6774ce035..04e7310e6bc3 100644 --- a/config.example.toml +++ b/config.example.toml @@ -922,6 +922,15 @@ # argument as the test binary. #runner = (string) +# Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics +# on this target. +# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt` +# sources are available. +# +# Setting this to `false` generates slower code, but removes the requirement for a C toolchain in +# order to run `x check`. +#optimized-compiler-builtins = build.optimized-compiler-builtins (bool) + # ============================================================================= # Distribution options # diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 0f66217b5cb3..1b5e44a91346 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -2028,8 +2028,7 @@ impl + ?Sized, A: Allocator> Fn for Box { } } -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] impl + ?Sized, A: Allocator> AsyncFnOnce for Box { type Output = F::Output; type CallOnceFuture = F::CallOnceFuture; @@ -2039,8 +2038,7 @@ impl + ?Sized, A: Allocator> AsyncFnOnce } } -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] impl + ?Sized, A: Allocator> AsyncFnMut for Box { type CallRefFuture<'a> = F::CallRefFuture<'a> @@ -2052,8 +2050,7 @@ impl + ?Sized, A: Allocator> AsyncFnMut f } } -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] impl + ?Sized, A: Allocator> AsyncFn for Box { extern "rust-call" fn async_call(&self, args: Args) -> Self::CallRefFuture<'_> { F::async_call(self, args) diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 0c93eff0d20f..4057657632ba 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -383,9 +383,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { /// Borrows a view into the keys stored in the node. pub fn keys(&self) -> &[K] { let leaf = self.into_leaf(); - unsafe { - MaybeUninit::slice_assume_init_ref(leaf.keys.get_unchecked(..usize::from(leaf.len))) - } + unsafe { leaf.keys.get_unchecked(..usize::from(leaf.len)).assume_init_ref() } } } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index aff90f5abb3a..b4f08debc932 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -91,7 +91,6 @@ // // Library features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(async_closure))] #![cfg_attr(test, feature(str_as_str))] #![feature(alloc_layout_extra)] #![feature(allocator_api)] @@ -127,6 +126,7 @@ #![feature(local_waker)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array_transpose)] +#![feature(nonnull_provenance)] #![feature(panic_internals)] #![feature(pattern)] #![feature(pin_coerce_unsized_trait)] @@ -143,6 +143,7 @@ #![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] +#![feature(temporary_niche_types)] #![feature(trusted_fused)] #![feature(trusted_len)] #![feature(trusted_random_access)] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index e93ff2f90237..ad86bf4bf072 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -33,21 +33,15 @@ enum AllocInit { Zeroed, } -#[repr(transparent)] -#[cfg_attr(target_pointer_width = "16", rustc_layout_scalar_valid_range_end(0x7fff))] -#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0x7fff_ffff))] -#[cfg_attr(target_pointer_width = "64", rustc_layout_scalar_valid_range_end(0x7fff_ffff_ffff_ffff))] -struct Cap(usize); +type Cap = core::num::niche_types::UsizeNoHighBit; -impl Cap { - const ZERO: Cap = unsafe { Cap(0) }; +const ZERO_CAP: Cap = unsafe { Cap::new_unchecked(0) }; - /// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. - /// - /// # Safety: cap must be <= `isize::MAX`. - unsafe fn new(cap: usize) -> Self { - if T::IS_ZST { Cap::ZERO } else { unsafe { Self(cap) } } - } +/// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. +/// +/// # Safety: cap must be <= `isize::MAX`. +unsafe fn new_cap(cap: usize) -> Cap { + if T::IS_ZST { ZERO_CAP } else { unsafe { Cap::new_unchecked(cap) } } } /// A low-level utility for more ergonomically allocating, reallocating, and deallocating @@ -257,7 +251,7 @@ impl RawVec { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); - let capacity = Cap::new::(capacity); + let capacity = new_cap::(capacity); Self { inner: RawVecInner::from_raw_parts_in(ptr, capacity, alloc), _marker: PhantomData, @@ -275,7 +269,7 @@ impl RawVec { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); - let capacity = Cap::new::(capacity); + let capacity = new_cap::(capacity); Self { inner: RawVecInner::from_nonnull_in(ptr, capacity, alloc), _marker: PhantomData } } } @@ -410,7 +404,7 @@ impl RawVecInner { const fn new_in(alloc: A, align: usize) -> Self { let ptr = unsafe { core::mem::transmute(align) }; // `cap: 0` means "unallocated". zero-sized types are ignored. - Self { ptr, cap: Cap::ZERO, alloc } + Self { ptr, cap: ZERO_CAP, alloc } } #[cfg(not(no_global_oom_handling))] @@ -483,7 +477,11 @@ impl RawVecInner { // Allocators currently return a `NonNull<[u8]>` whose length // matches the size requested. If that ever changes, the capacity // here should change to `ptr.len() / mem::size_of::()`. - Ok(Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc }) + Ok(Self { + ptr: Unique::from(ptr.cast()), + cap: unsafe { Cap::new_unchecked(capacity) }, + alloc, + }) } #[inline] @@ -508,7 +506,7 @@ impl RawVecInner { #[inline] const fn capacity(&self, elem_size: usize) -> usize { - if elem_size == 0 { usize::MAX } else { self.cap.0 } + if elem_size == 0 { usize::MAX } else { self.cap.as_inner() } } #[inline] @@ -518,7 +516,7 @@ impl RawVecInner { #[inline] fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull, Layout)> { - if elem_layout.size() == 0 || self.cap.0 == 0 { + if elem_layout.size() == 0 || self.cap.as_inner() == 0 { None } else { // We could use Layout::array here which ensures the absence of isize and usize overflows @@ -526,7 +524,7 @@ impl RawVecInner { // has already been allocated so we know it can't overflow and currently Rust does not // support such types. So we can do better by skipping some checks and avoid an unwrap. unsafe { - let alloc_size = elem_layout.size().unchecked_mul(self.cap.0); + let alloc_size = elem_layout.size().unchecked_mul(self.cap.as_inner()); let layout = Layout::from_size_align_unchecked(alloc_size, elem_layout.align()); Some((self.ptr.into(), layout)) } @@ -562,7 +560,7 @@ impl RawVecInner { #[inline] #[track_caller] fn grow_one(&mut self, elem_layout: Layout) { - if let Err(err) = self.grow_amortized(self.cap.0, 1, elem_layout) { + if let Err(err) = self.grow_amortized(self.cap.as_inner(), 1, elem_layout) { handle_error(err); } } @@ -627,7 +625,7 @@ impl RawVecInner { // the size requested. If that ever changes, the capacity here should // change to `ptr.len() / mem::size_of::()`. self.ptr = Unique::from(ptr.cast()); - self.cap = unsafe { Cap(cap) }; + self.cap = unsafe { Cap::new_unchecked(cap) }; } fn grow_amortized( @@ -650,7 +648,7 @@ impl RawVecInner { // This guarantees exponential growth. The doubling cannot overflow // because `cap <= isize::MAX` and the type of `cap` is `usize`. - let cap = cmp::max(self.cap.0 * 2, required_cap); + let cap = cmp::max(self.cap.as_inner() * 2, required_cap); let cap = cmp::max(min_non_zero_cap(elem_layout.size()), cap); let new_layout = layout_array(cap, elem_layout)?; @@ -719,7 +717,7 @@ impl RawVecInner { unsafe { self.alloc.deallocate(ptr, layout) }; self.ptr = unsafe { Unique::new_unchecked(ptr::without_provenance_mut(elem_layout.align())) }; - self.cap = Cap::ZERO; + self.cap = ZERO_CAP; } else { let ptr = unsafe { // Layout cannot overflow here because it would have diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index e014404eff35..ae3318b839dd 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -252,6 +252,7 @@ use core::intrinsics::abort; use core::iter; use core::marker::{PhantomData, Unsize}; use core::mem::{self, ManuallyDrop, align_of_val_raw}; +use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; #[cfg(not(no_global_oom_handling))] @@ -1789,7 +1790,7 @@ impl Rc { /// let x: Rc<&str> = Rc::new("Hello, world!"); /// { /// let s = String::from("Oh, no!"); - /// let mut y: Rc<&str> = x.clone().into(); + /// let mut y: Rc<&str> = x.clone(); /// unsafe { /// // this is Undefined Behavior, because x's inner type /// // is &'long str, not &'short str @@ -3027,12 +3028,7 @@ impl Weak { #[rustc_const_stable(feature = "const_weak_new", since = "1.73.0")] #[must_use] pub const fn new() -> Weak { - Weak { - ptr: unsafe { - NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) - }, - alloc: Global, - } + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc: Global } } } @@ -3054,12 +3050,7 @@ impl Weak { #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub fn new_in(alloc: A) -> Weak { - Weak { - ptr: unsafe { - NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) - }, - alloc, - } + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc } } } @@ -3717,7 +3708,11 @@ pub struct UniqueRc< #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, > { ptr: NonNull>, - phantom: PhantomData>, + // Define the ownership of `RcInner` for drop-check + _marker: PhantomData>, + // Invariance is necessary for soundness: once other `Weak` + // references exist, we already have a form of shared mutability! + _marker2: PhantomData<*mut T>, alloc: A, } @@ -4003,7 +3998,7 @@ impl UniqueRc { }, alloc, )); - Self { ptr: ptr.into(), phantom: PhantomData, alloc } + Self { ptr: ptr.into(), _marker: PhantomData, _marker2: PhantomData, alloc } } } 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/alloc/src/sync.rs b/library/alloc/src/sync.rs index b34a6d3f660c..11e7128e6777 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -18,6 +18,7 @@ use core::intrinsics::abort; use core::iter; use core::marker::{PhantomData, Unsize}; use core::mem::{self, ManuallyDrop, align_of_val_raw}; +use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, LegacyReceiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::pin::{Pin, PinCoerceUnsized}; @@ -2468,7 +2469,7 @@ impl Arc { /// let x: Arc<&str> = Arc::new("Hello, world!"); /// { /// let s = String::from("Oh, no!"); - /// let mut y: Arc<&str> = x.clone().into(); + /// let mut y: Arc<&str> = x.clone(); /// unsafe { /// // this is Undefined Behavior, because x's inner type /// // is &'long str, not &'short str @@ -2687,12 +2688,7 @@ impl Weak { #[rustc_const_stable(feature = "const_weak_new", since = "1.73.0")] #[must_use] pub const fn new() -> Weak { - Weak { - ptr: unsafe { - NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) - }, - alloc: Global, - } + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc: Global } } } @@ -2717,12 +2713,7 @@ impl Weak { #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub fn new_in(alloc: A) -> Weak { - Weak { - ptr: unsafe { - NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) - }, - alloc, - } + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc } } } diff --git a/library/alloc/src/vec/is_zero.rs b/library/alloc/src/vec/is_zero.rs index ba57d940d8c9..a3ddd6f6e230 100644 --- a/library/alloc/src/vec/is_zero.rs +++ b/library/alloc/src/vec/is_zero.rs @@ -40,19 +40,8 @@ impl_is_zero!(char, |x| x == '\0'); impl_is_zero!(f32, |x: f32| x.to_bits() == 0); impl_is_zero!(f64, |x: f64| x.to_bits() == 0); -unsafe impl IsZero for *const T { - #[inline] - fn is_zero(&self) -> bool { - (*self).is_null() - } -} - -unsafe impl IsZero for *mut T { - #[inline] - fn is_zero(&self) -> bool { - (*self).is_null() - } -} +// `IsZero` cannot be soundly implemented for pointers because of provenance +// (see #135338). unsafe impl IsZero for [T; N] { #[inline] diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 2e654d3d1ff1..b24daec2968e 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -2742,3 +2742,13 @@ fn max_swap_remove() { let mut v = vec![0]; v.swap_remove(usize::MAX); } + +// Regression test for #135338 +#[test] +fn vec_null_ptr_roundtrip() { + let ptr = std::ptr::from_ref(&42); + let zero = ptr.with_addr(0); + let roundtripped = vec![zero; 1].pop().unwrap(); + let new = roundtripped.with_addr(ptr.addr()); + unsafe { new.read() }; +} diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index e6f39db9dced..17f4d68867e1 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -178,7 +178,7 @@ impl Layout { /// allocate backing structure for `T` (which could be a trait /// or other unsized type like a slice). #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[must_use] #[inline] pub const fn for_value(t: &T) -> Self { @@ -233,8 +233,7 @@ impl Layout { #[must_use] #[inline] pub const fn dangling(&self) -> NonNull { - // SAFETY: align is guaranteed to be non-zero - unsafe { NonNull::new_unchecked(crate::ptr::without_provenance_mut::(self.align())) } + NonNull::without_provenance(self.align.as_nonzero()) } /// Creates a layout describing the record that can hold a value @@ -252,7 +251,7 @@ impl Layout { /// Returns an error if the combination of `self.size()` and the given /// `align` violates the conditions listed in [`Layout::from_size_align`]. #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[inline] pub const fn align_to(&self, align: usize) -> Result { if let Some(align) = Alignment::new(align) { @@ -327,7 +326,7 @@ impl Layout { /// This is equivalent to adding the result of `padding_needed_for` /// to the layout's current size. #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[must_use = "this returns a new `Layout`, \ without modifying the original"] #[inline] @@ -426,7 +425,7 @@ impl Layout { /// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16]))); /// ``` #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[inline] pub const fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> { let new_align = Alignment::max(self.align, next.align); @@ -489,7 +488,7 @@ impl Layout { /// On arithmetic overflow or when the total size would exceed /// `isize::MAX`, returns `LayoutError`. #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[inline] pub const fn array(n: usize) -> Result { // Reduce the amount of code we need to monomorphize per `T`. diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index 95d88c7f6799..cb130f60cecf 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -65,7 +65,6 @@ pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))? // When stabilizing this, update the comment on `core::intrinsics::breakpoint`. #[unstable(feature = "breakpoint", issue = "133724")] #[inline(always)] -#[cfg(not(bootstrap))] pub fn breakpoint() { core::intrinsics::breakpoint(); } diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 9ce0eb61e081..1edade41597f 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -214,7 +214,7 @@ impl IntoIter { // SAFETY: We know that all elements within `alive` are properly initialized. unsafe { let slice = self.data.get_unchecked(self.alive.clone()); - MaybeUninit::slice_assume_init_ref(slice) + slice.assume_init_ref() } } @@ -224,7 +224,7 @@ impl IntoIter { // SAFETY: We know that all elements within `alive` are properly initialized. unsafe { let slice = self.data.get_unchecked_mut(self.alive.clone()); - MaybeUninit::slice_assume_init_mut(slice) + slice.assume_init_mut() } } } @@ -285,7 +285,7 @@ impl Iterator for IntoIter { // SAFETY: These elements are currently initialized, so it's fine to drop them. unsafe { let slice = self.data.get_unchecked_mut(range_to_drop); - ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice)); + slice.assume_init_drop(); } NonZero::new(remaining).map_or(Ok(()), Err) @@ -340,7 +340,7 @@ impl DoubleEndedIterator for IntoIter { // SAFETY: These elements are currently initialized, so it's fine to drop them. unsafe { let slice = self.data.get_unchecked_mut(range_to_drop); - ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice)); + slice.assume_init_drop(); } NonZero::new(remaining).map_or(Ok(()), Err) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 95c1eb460cd9..2ae5ded1fd55 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -911,9 +911,7 @@ impl Drop for Guard<'_, T> { // SAFETY: this slice will contain only initialized objects. unsafe { - crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( - self.array_mut.get_unchecked_mut(..self.initialized), - )); + self.array_mut.get_unchecked_mut(..self.initialized).assume_init_drop(); } } } 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/future/async_drop.rs b/library/core/src/future/async_drop.rs index ea6d3b800e64..f1778a4d782a 100644 --- a/library/core/src/future/async_drop.rs +++ b/library/core/src/future/async_drop.rs @@ -133,9 +133,8 @@ pub trait AsyncDrop { } #[lang = "async_destruct"] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] trait AsyncDestruct { type AsyncDestructor: Future; } diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index 84bbf985e8be..7a6630c82d0d 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -752,11 +752,8 @@ pub struct BuildHasherDefault(marker::PhantomData H>); impl BuildHasherDefault { /// Creates a new BuildHasherDefault for Hasher `H`. - #[stable(feature = "build_hasher_default_const_new", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable( - feature = "build_hasher_default_const_new", - since = "CURRENT_RUSTC_VERSION" - )] + #[stable(feature = "build_hasher_default_const_new", since = "1.85.0")] + #[rustc_const_stable(feature = "build_hasher_default_const_new", since = "1.85.0")] pub const fn new() -> Self { BuildHasherDefault(marker::PhantomData) } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 7d44ea7a66ba..7b31bbec7547 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1382,22 +1382,10 @@ pub unsafe fn prefetch_write_instruction(_data: *const T, _locality: i32) { #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] #[rustc_nounwind] -#[cfg(not(bootstrap))] pub fn breakpoint() { unreachable!() } -/// Executes a breakpoint trap, for inspection by a debugger. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -#[cfg(bootstrap)] -pub unsafe fn breakpoint() { - unreachable!() -} - /// Magic intrinsic that derives its meaning from attributes /// attached to the function. /// @@ -3323,8 +3311,8 @@ pub const fn mul_with_overflow(_x: T, _y: T) -> (T, bool) { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_const_unstable(feature = "const_carrying_mul_add", issue = "85532")] #[rustc_nounwind] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)] +#[rustc_intrinsic] +#[miri::intrinsic_fallback_is_spec] pub const fn carrying_mul_add, U>( multiplier: T, multiplicand: T, @@ -3969,21 +3957,6 @@ pub const fn is_val_statically_known(_arg: T) -> bool { false } -#[rustc_nounwind] -#[inline] -#[rustc_intrinsic] -#[rustc_intrinsic_const_stable_indirect] -#[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic -#[cfg(bootstrap)] -pub const unsafe fn typed_swap(x: *mut T, y: *mut T) { - // SAFETY: The caller provided single non-overlapping items behind - // pointers, so swapping them with `count: 1` is fine. - unsafe { ptr::swap_nonoverlapping(x, y, 1) }; -} - -#[cfg(bootstrap)] -pub use typed_swap as typed_swap_nonoverlapping; - /// Non-overlapping *typed* swap of a single value. /// /// The codegen backends will replace this with a better implementation when @@ -3999,7 +3972,6 @@ pub use typed_swap as typed_swap_nonoverlapping; #[rustc_intrinsic] #[rustc_intrinsic_const_stable_indirect] #[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic -#[cfg(not(bootstrap))] pub const unsafe fn typed_swap_nonoverlapping(x: *mut T, y: *mut T) { // SAFETY: The caller provided single non-overlapping items behind // pointers, so swapping them with `count: 1` is fine. diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index f80a60d471c0..d03d801b9365 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -623,7 +623,6 @@ extern "rust-intrinsic" { /// and others do not. /// /// `T` must be a vector of floats. - #[cfg(not(bootstrap))] #[rustc_nounwind] pub fn simd_relaxed_fma(x: T, y: T, z: T) -> T; diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index 4227e503ba7b..f86abf7f1e91 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -94,7 +94,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked(..self.filled); - MaybeUninit::slice_assume_init_ref(buf) + buf.assume_init_ref() } } @@ -104,7 +104,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked_mut(..self.filled); - MaybeUninit::slice_assume_init_mut(buf) + buf.assume_init_mut() } } @@ -114,7 +114,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked(..self.filled); - MaybeUninit::slice_assume_init_ref(buf) + buf.assume_init_ref() } } @@ -124,7 +124,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked_mut(..self.filled); - MaybeUninit::slice_assume_init_mut(buf) + buf.assume_init_mut() } } @@ -233,7 +233,7 @@ impl<'a> BorrowedCursor<'a> { // SAFETY: We only slice the initialized part of the buffer, which is always valid unsafe { let buf = self.buf.buf.get_unchecked(self.buf.filled..self.buf.init); - MaybeUninit::slice_assume_init_ref(buf) + buf.assume_init_ref() } } @@ -243,7 +243,7 @@ impl<'a> BorrowedCursor<'a> { // SAFETY: We only slice the initialized part of the buffer, which is always valid unsafe { let buf = self.buf.buf.get_unchecked_mut(self.buf.filled..self.buf.init); - MaybeUninit::slice_assume_init_mut(buf) + buf.assume_init_mut() } } @@ -344,7 +344,7 @@ impl<'a> BorrowedCursor<'a> { // SAFETY: we do not de-initialize any of the elements of the slice unsafe { - MaybeUninit::copy_from_slice(&mut self.as_mut()[..buf.len()], buf); + self.as_mut()[..buf.len()].write_copy_of_slice(buf); } // SAFETY: We just added the entire contents of buf to the filled section. diff --git a/library/core/src/iter/adapters/filter_map.rs b/library/core/src/iter/adapters/filter_map.rs index cc64ceb13f76..24ec6b1741ce 100644 --- a/library/core/src/iter/adapters/filter_map.rs +++ b/library/core/src/iter/adapters/filter_map.rs @@ -81,9 +81,7 @@ where if const { crate::mem::needs_drop::() } { // SAFETY: self.initialized is always <= N, which also is the length of the array. unsafe { - core::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( - self.array.get_unchecked_mut(..self.initialized), - )); + self.array.get_unchecked_mut(..self.initialized).assume_init_drop(); } } } diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 73e6d9310609..97bb21c8a36e 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -472,7 +472,7 @@ macro_rules! spec_tuple_impl { #[doc(fake_variadic)] #[doc = "This trait is implemented for tuples up to twelve items long. The `impl`s for \ 1- and 3- through 12-ary tuples were stabilized after 2-tuples, in \ - CURRENT_RUSTC_VERSION."] + 1.85.0."] => ($ty_name, $var_name, $extend_ty_name, $cnt), ); }; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index e31957a1fa70..e845bb34426c 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -107,7 +107,6 @@ // // Library features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(do_not_recommend))] #![feature(array_ptr_get)] #![feature(asm_experimental_arch)] #![feature(bigint_helper_methods)] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 29fc01d37fe3..4af9b666e54f 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -141,9 +141,8 @@ unsafe impl Send for &T {} )] #[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable #[rustc_specialization_trait] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] #[rustc_coinductive] pub trait Sized { // Empty. @@ -183,33 +182,27 @@ pub trait Sized { /// [^1]: Formerly known as *object safe*. #[unstable(feature = "unsize", issue = "18598")] #[lang = "unsize"] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait Unsize { // Empty. } /// Required trait for constants used in pattern matches. /// -/// Any type that derives `PartialEq` automatically implements this trait, -/// *regardless* of whether its type-parameters implement `PartialEq`. +/// Constants are only allowed as patterns if (a) their type implements +/// `PartialEq`, and (b) interpreting the value of the constant as a pattern +/// is equialent to calling `PartialEq`. This ensures that constants used as +/// patterns cannot expose implementation details in an unexpected way or +/// cause semver hazards. /// -/// If a `const` item contains some type that does not implement this trait, -/// then that type either (1.) does not implement `PartialEq` (which means the -/// constant will not provide that comparison method, which code generation -/// assumes is available), or (2.) it implements *its own* version of -/// `PartialEq` (which we assume does not conform to a structural-equality -/// comparison). +/// This trait ensures point (b). +/// Any type that derives `PartialEq` automatically implements this trait. /// -/// In either of the two scenarios above, we reject usage of such a constant in -/// a pattern match. -/// -/// See also the [structural match RFC][RFC1445], and [issue 63438] which -/// motivated migrating from an attribute-based design to this trait. -/// -/// [RFC1445]: https://github.com/rust-lang/rfcs/blob/master/text/1445-restrict-constants-in-patterns.md -/// [issue 63438]: https://github.com/rust-lang/rust/issues/63438 +/// Implementing this trait (which is unstable) is a way for type authors to explicitly allow +/// comparing const values of this type; that operation will recursively compare all fields +/// (including private fields), even if that behavior differs from `PartialEq`. This can make it +/// semver-breaking to add further private fields to a type. #[unstable(feature = "structural_match", issue = "31434")] #[diagnostic::on_unimplemented(message = "the type `{Self}` does not `#[derive(PartialEq)]`")] #[lang = "structural_peq"] @@ -819,9 +812,8 @@ impl StructuralPartialEq for PhantomData {} reason = "this trait is unlikely to ever be stabilized, use `mem::discriminant` instead" )] #[lang = "discriminant_kind"] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait DiscriminantKind { /// The type of the discriminant, which must satisfy the trait /// bounds required by `mem::Discriminant`. @@ -962,10 +954,9 @@ marker_impls! { #[unstable(feature = "const_destruct", issue = "133214")] #[lang = "destruct"] #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] -#[cfg_attr(not(bootstrap), const_trait)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] +#[const_trait] pub trait Destruct {} /// A marker for tuple types. @@ -975,9 +966,8 @@ pub trait Destruct {} #[unstable(feature = "tuple_trait", issue = "none")] #[lang = "tuple_trait"] #[diagnostic::on_unimplemented(message = "`{Self}` is not a tuple")] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait Tuple {} /// A marker for pointer-like types. @@ -996,10 +986,9 @@ pub trait Tuple {} message = "`{Self}` needs to have the same ABI as a pointer", label = "`{Self}` needs to be a pointer-like type" )] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_do_not_implement_via_object] pub trait PointerLike {} -#[cfg(not(bootstrap))] marker_impls! { #[unstable(feature = "pointer_like_trait", issue = "none")] PointerLike for @@ -1086,9 +1075,8 @@ marker_impls! { reason = "internal trait for implementing various traits for all function pointers" )] #[lang = "fn_ptr_trait"] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait FnPtr: Copy + Clone { /// Returns the address of the function pointer. #[lang = "fn_ptr_addr"] @@ -1099,7 +1087,6 @@ pub trait FnPtr: Copy + Clone { #[rustc_builtin_macro(CoercePointee, attributes(pointee))] #[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)] #[unstable(feature = "derive_coerce_pointee", issue = "123430")] -#[cfg(not(bootstrap))] pub macro CoercePointee($item:item) { /* compiler built-in */ } diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 58fb5be5812c..ac5307a671d2 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1,5 +1,5 @@ use crate::any::type_name; -use crate::mem::{self, ManuallyDrop}; +use crate::mem::ManuallyDrop; use crate::{fmt, intrinsics, ptr, slice}; /// A wrapper type to construct uninitialized instances of `T`. @@ -354,7 +354,7 @@ impl MaybeUninit { /// fn read(buf: &mut [MaybeUninit]) -> &[u8] { /// unsafe { /// let len = read_into_buffer(buf.as_mut_ptr() as *mut u8, buf.len()); - /// MaybeUninit::slice_assume_init_ref(&buf[..len]) + /// buf[..len].assume_init_ref() /// } /// } /// @@ -507,7 +507,7 @@ impl MaybeUninit { /// ``` #[inline(always)] #[stable(feature = "maybe_uninit_write", since = "1.55.0")] - #[rustc_const_stable(feature = "const_maybe_uninit_write", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_maybe_uninit_write", since = "1.85.0")] pub const fn write(&mut self, val: T) -> &mut T { *self = MaybeUninit::new(val); // SAFETY: We just initialized this value. @@ -740,7 +740,7 @@ impl MaybeUninit { /// /// On top of that, all additional invariants of the type `T` must be /// satisfied, as the `Drop` implementation of `T` (or its members) may - /// rely on this. For example, setting a [`Vec`] to an invalid but + /// rely on this. For example, setting a `Vec` to an invalid but /// non-null address makes it initialized (under the current implementation; /// this does not constitute a stable guarantee), because the only /// requirement the compiler knows about it is that the data pointer must be @@ -748,7 +748,6 @@ impl MaybeUninit { /// behavior. /// /// [`assume_init`]: MaybeUninit::assume_init - /// [`Vec`]: ../../std/vec/struct.Vec.html #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] pub unsafe fn assume_init_drop(&mut self) { // SAFETY: the caller must guarantee that `self` is initialized and @@ -982,339 +981,6 @@ impl MaybeUninit { } } - /// Assuming all the elements are initialized, get a slice to them. - /// - /// # Safety - /// - /// It is up to the caller to guarantee that the `MaybeUninit` elements - /// really are in an initialized state. - /// Calling this when the content is not yet fully initialized causes undefined behavior. - /// - /// See [`assume_init_ref`] for more details and examples. - /// - /// [`assume_init_ref`]: MaybeUninit::assume_init_ref - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] - pub const unsafe fn slice_assume_init_ref(slice: &[Self]) -> &[T] { - // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that - // `slice` is initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`. - // The pointer obtained is valid since it refers to memory owned by `slice` which is a - // reference and thus guaranteed to be valid for reads. - unsafe { &*(slice as *const [Self] as *const [T]) } - } - - /// Assuming all the elements are initialized, get a mutable slice to them. - /// - /// # Safety - /// - /// It is up to the caller to guarantee that the `MaybeUninit` elements - /// really are in an initialized state. - /// Calling this when the content is not yet fully initialized causes undefined behavior. - /// - /// See [`assume_init_mut`] for more details and examples. - /// - /// [`assume_init_mut`]: MaybeUninit::assume_init_mut - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] - pub const unsafe fn slice_assume_init_mut(slice: &mut [Self]) -> &mut [T] { - // SAFETY: similar to safety notes for `slice_get_ref`, but we have a - // mutable reference which is also guaranteed to be valid for writes. - unsafe { &mut *(slice as *mut [Self] as *mut [T]) } - } - - /// Gets a pointer to the first element of the array. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] - pub const fn slice_as_ptr(this: &[MaybeUninit]) -> *const T { - this.as_ptr() as *const T - } - - /// Gets a mutable pointer to the first element of the array. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] - pub const fn slice_as_mut_ptr(this: &mut [MaybeUninit]) -> *mut T { - this.as_mut_ptr() as *mut T - } - - /// Copies the elements from `src` to `this`, returning a mutable reference to the now initialized contents of `this`. - /// - /// If `T` does not implement `Copy`, use [`clone_from_slice`] - /// - /// This is similar to [`slice::copy_from_slice`]. - /// - /// # Panics - /// - /// This function will panic if the two slices have different lengths. - /// - /// # Examples - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// use std::mem::MaybeUninit; - /// - /// let mut dst = [MaybeUninit::uninit(); 32]; - /// let src = [0; 32]; - /// - /// let init = MaybeUninit::copy_from_slice(&mut dst, &src); - /// - /// assert_eq!(init, src); - /// ``` - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// use std::mem::MaybeUninit; - /// - /// let mut vec = Vec::with_capacity(32); - /// let src = [0; 16]; - /// - /// MaybeUninit::copy_from_slice(&mut vec.spare_capacity_mut()[..src.len()], &src); - /// - /// // SAFETY: we have just copied all the elements of len into the spare capacity - /// // the first src.len() elements of the vec are valid now. - /// unsafe { - /// vec.set_len(src.len()); - /// } - /// - /// assert_eq!(vec, src); - /// ``` - /// - /// [`clone_from_slice`]: MaybeUninit::clone_from_slice - #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] - pub fn copy_from_slice<'a>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] - where - T: Copy, - { - // SAFETY: &[T] and &[MaybeUninit] have the same layout - let uninit_src: &[MaybeUninit] = unsafe { super::transmute(src) }; - - this.copy_from_slice(uninit_src); - - // SAFETY: Valid elements have just been copied into `this` so it is initialized - unsafe { MaybeUninit::slice_assume_init_mut(this) } - } - - /// Clones the elements from `src` to `this`, returning a mutable reference to the now initialized contents of `this`. - /// Any already initialized elements will not be dropped. - /// - /// If `T` implements `Copy`, use [`copy_from_slice`] - /// - /// This is similar to [`slice::clone_from_slice`] but does not drop existing elements. - /// - /// # Panics - /// - /// This function will panic if the two slices have different lengths, or if the implementation of `Clone` panics. - /// - /// If there is a panic, the already cloned elements will be dropped. - /// - /// # Examples - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// use std::mem::MaybeUninit; - /// - /// let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()]; - /// let src = ["wibbly".to_string(), "wobbly".to_string(), "timey".to_string(), "wimey".to_string(), "stuff".to_string()]; - /// - /// let init = MaybeUninit::clone_from_slice(&mut dst, &src); - /// - /// assert_eq!(init, src); - /// # // Prevent leaks for Miri - /// # unsafe { std::ptr::drop_in_place(init); } - /// ``` - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// use std::mem::MaybeUninit; - /// - /// let mut vec = Vec::with_capacity(32); - /// let src = ["rust", "is", "a", "pretty", "cool", "language"]; - /// - /// MaybeUninit::clone_from_slice(&mut vec.spare_capacity_mut()[..src.len()], &src); - /// - /// // SAFETY: we have just cloned all the elements of len into the spare capacity - /// // the first src.len() elements of the vec are valid now. - /// unsafe { - /// vec.set_len(src.len()); - /// } - /// - /// assert_eq!(vec, src); - /// ``` - /// - /// [`copy_from_slice`]: MaybeUninit::copy_from_slice - #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] - pub fn clone_from_slice<'a>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] - where - T: Clone, - { - // unlike copy_from_slice this does not call clone_from_slice on the slice - // this is because `MaybeUninit` does not implement Clone. - - assert_eq!(this.len(), src.len(), "destination and source slices have different lengths"); - // NOTE: We need to explicitly slice them to the same length - // for bounds checking to be elided, and the optimizer will - // generate memcpy for simple cases (for example T = u8). - let len = this.len(); - let src = &src[..len]; - - // guard is needed b/c panic might happen during a clone - let mut guard = Guard { slice: this, initialized: 0 }; - - for i in 0..len { - guard.slice[i].write(src[i].clone()); - guard.initialized += 1; - } - - super::forget(guard); - - // SAFETY: Valid elements have just been written into `this` so it is initialized - unsafe { MaybeUninit::slice_assume_init_mut(this) } - } - - /// Fills `this` with elements by cloning `value`, returning a mutable reference to the now - /// initialized contents of `this`. - /// Any previously initialized elements will not be dropped. - /// - /// This is similar to [`slice::fill`]. - /// - /// # Panics - /// - /// This function will panic if any call to `Clone` panics. - /// - /// If such a panic occurs, any elements previously initialized during this operation will be - /// dropped. - /// - /// # Examples - /// - /// Fill an uninit vec with 1. - /// ``` - /// #![feature(maybe_uninit_fill)] - /// use std::mem::MaybeUninit; - /// - /// let mut buf = vec![MaybeUninit::uninit(); 10]; - /// let initialized = MaybeUninit::fill(buf.as_mut_slice(), 1); - /// assert_eq!(initialized, &mut [1; 10]); - /// ``` - #[doc(alias = "memset")] - #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill<'a>(this: &'a mut [MaybeUninit], value: T) -> &'a mut [T] - where - T: Clone, - { - SpecFill::spec_fill(this, value); - // SAFETY: Valid elements have just been filled into `this` so it is initialized - unsafe { MaybeUninit::slice_assume_init_mut(this) } - } - - /// Fills `this` with elements returned by calling a closure repeatedly. - /// - /// This method uses a closure to create new values. If you'd rather `Clone` a given value, use - /// [`MaybeUninit::fill`]. If you want to use the `Default` trait to generate values, you can - /// pass [`Default::default`] as the argument. - /// - /// # Panics - /// - /// This function will panic if any call to the provided closure panics. - /// - /// If such a panic occurs, any elements previously initialized during this operation will be - /// dropped. - /// - /// # Examples - /// - /// Fill an uninit vec with the default value. - /// ``` - /// #![feature(maybe_uninit_fill)] - /// use std::mem::MaybeUninit; - /// - /// let mut buf = vec![MaybeUninit::::uninit(); 10]; - /// let initialized = MaybeUninit::fill_with(buf.as_mut_slice(), Default::default); - /// assert_eq!(initialized, &mut [0; 10]); - /// ``` - #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill_with<'a, F>(this: &'a mut [MaybeUninit], mut f: F) -> &'a mut [T] - where - F: FnMut() -> T, - { - let mut guard = Guard { slice: this, initialized: 0 }; - - for element in guard.slice.iter_mut() { - element.write(f()); - guard.initialized += 1; - } - - super::forget(guard); - - // SAFETY: Valid elements have just been written into `this` so it is initialized - unsafe { MaybeUninit::slice_assume_init_mut(this) } - } - - /// Fills `this` with elements yielded by an iterator until either all elements have been - /// initialized or the iterator is empty. - /// - /// Returns two slices. The first slice contains the initialized portion of the original slice. - /// The second slice is the still-uninitialized remainder of the original slice. - /// - /// # Panics - /// - /// This function panics if the iterator's `next` function panics. - /// - /// If such a panic occurs, any elements previously initialized during this operation will be - /// dropped. - /// - /// # Examples - /// - /// Fill an uninit vec with a cycling iterator. - /// ``` - /// #![feature(maybe_uninit_fill)] - /// use std::mem::MaybeUninit; - /// - /// let mut buf = vec![MaybeUninit::uninit(); 5]; - /// - /// let iter = [1, 2, 3].into_iter().cycle(); - /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); - /// - /// assert_eq!(initialized, &mut [1, 2, 3, 1, 2]); - /// assert_eq!(0, remainder.len()); - /// ``` - /// - /// Fill an uninit vec, but not completely. - /// ``` - /// #![feature(maybe_uninit_fill)] - /// use std::mem::MaybeUninit; - /// - /// let mut buf = vec![MaybeUninit::uninit(); 5]; - /// let iter = [1, 2]; - /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); - /// - /// assert_eq!(initialized, &mut [1, 2]); - /// assert_eq!(remainder.len(), 3); - /// ``` - #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill_from<'a, I>( - this: &'a mut [MaybeUninit], - it: I, - ) -> (&'a mut [T], &'a mut [MaybeUninit]) - where - I: IntoIterator, - { - let iter = it.into_iter(); - let mut guard = Guard { slice: this, initialized: 0 }; - - for (element, val) in guard.slice.iter_mut().zip(iter) { - element.write(val); - guard.initialized += 1; - } - - let initialized_len = guard.initialized; - super::forget(guard); - - // SAFETY: guard.initialized <= this.len() - let (initted, remainder) = unsafe { this.split_at_mut_unchecked(initialized_len) }; - - // SAFETY: Valid elements have just been written into `init`, so that portion - // of `this` is initialized. - (unsafe { MaybeUninit::slice_assume_init_mut(initted) }, remainder) - } - /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. /// /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still @@ -1329,14 +995,14 @@ impl MaybeUninit { /// let val = 0x12345678_i32; /// let uninit = MaybeUninit::new(val); /// let uninit_bytes = uninit.as_bytes(); - /// let bytes = unsafe { MaybeUninit::slice_assume_init_ref(uninit_bytes) }; + /// let bytes = unsafe { uninit_bytes.assume_init_ref() }; /// assert_eq!(bytes, val.to_ne_bytes()); /// ``` #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub fn as_bytes(&self) -> &[MaybeUninit] { + pub const fn as_bytes(&self) -> &[MaybeUninit] { // SAFETY: MaybeUninit is always valid, even for padding bytes unsafe { - slice::from_raw_parts(self.as_ptr() as *const MaybeUninit, mem::size_of::()) + slice::from_raw_parts(self.as_ptr().cast::>(), super::size_of::()) } } @@ -1364,18 +1030,397 @@ impl MaybeUninit { /// assert_eq!(val2, 0x123456cd); /// ``` #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { + pub const fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { // SAFETY: MaybeUninit is always valid, even for padding bytes unsafe { slice::from_raw_parts_mut( - self.as_mut_ptr() as *mut MaybeUninit, - mem::size_of::(), + self.as_mut_ptr().cast::>(), + super::size_of::(), ) } } - /// Returns the contents of this slice of `MaybeUninit` as a slice of potentially uninitialized - /// bytes. + /// Deprecated version of [`slice::assume_init_ref`]. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[rustc_const_unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[deprecated( + note = "replaced by inherent assume_init_ref method; will eventually be removed", + since = "1.83.0" + )] + pub const unsafe fn slice_assume_init_ref(slice: &[Self]) -> &[T] { + // SAFETY: Same for both methods. + unsafe { slice.assume_init_ref() } + } + + /// Deprecated version of [`slice::assume_init_mut`]. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[rustc_const_unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[deprecated( + note = "replaced by inherent assume_init_mut method; will eventually be removed", + since = "1.83.0" + )] + pub const unsafe fn slice_assume_init_mut(slice: &mut [Self]) -> &mut [T] { + // SAFETY: Same for both methods. + unsafe { slice.assume_init_mut() } + } + + /// Gets a pointer to the first element of the array. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[inline(always)] + pub const fn slice_as_ptr(this: &[MaybeUninit]) -> *const T { + this.as_ptr() as *const T + } + + /// Gets a mutable pointer to the first element of the array. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[inline(always)] + pub const fn slice_as_mut_ptr(this: &mut [MaybeUninit]) -> *mut T { + this.as_mut_ptr() as *mut T + } + + /// Deprecated version of [`slice::write_copy_of_slice`]. + #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[deprecated( + note = "replaced by inherent write_copy_of_slice method; will eventually be removed", + since = "1.83.0" + )] + pub fn copy_from_slice<'a>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] + where + T: Copy, + { + this.write_copy_of_slice(src) + } + + /// Deprecated version of [`slice::write_clone_of_slice`]. + #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[deprecated( + note = "replaced by inherent write_clone_of_slice method; will eventually be removed", + since = "1.83.0" + )] + pub fn clone_from_slice<'a>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] + where + T: Clone, + { + this.write_clone_of_slice(src) + } + + /// Fills a slice with elements by cloning `value`, returning a mutable reference to the now + /// initialized contents of the slice. + /// Any previously initialized elements will not be dropped. + /// + /// This is similar to [`slice::fill`]. + /// + /// # Panics + /// + /// This function will panic if any call to `Clone` panics. + /// + /// If such a panic occurs, any elements previously initialized during this operation will be + /// dropped. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = [const { MaybeUninit::uninit() }; 10]; + /// let initialized = MaybeUninit::fill(&mut buf, 1); + /// assert_eq!(initialized, &mut [1; 10]); + /// ``` + #[doc(alias = "memset")] + #[unstable(feature = "maybe_uninit_fill", issue = "117428")] + pub fn fill(this: &mut [MaybeUninit], value: T) -> &mut [T] + where + T: Clone, + { + SpecFill::spec_fill(this, value); + // SAFETY: Valid elements have just been filled into `this` so it is initialized + unsafe { this.assume_init_mut() } + } + + /// Fills a slice with elements returned by calling a closure repeatedly. + /// + /// This method uses a closure to create new values. If you'd rather `Clone` a given value, use + /// [`MaybeUninit::fill`]. If you want to use the `Default` trait to generate values, you can + /// pass [`Default::default`] as the argument. + /// + /// # Panics + /// + /// This function will panic if any call to the provided closure panics. + /// + /// If such a panic occurs, any elements previously initialized during this operation will be + /// dropped. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = [const { MaybeUninit::::uninit() }; 10]; + /// let initialized = MaybeUninit::fill_with(&mut buf, Default::default); + /// assert_eq!(initialized, &mut [0; 10]); + /// ``` + #[unstable(feature = "maybe_uninit_fill", issue = "117428")] + pub fn fill_with(this: &mut [MaybeUninit], mut f: F) -> &mut [T] + where + F: FnMut() -> T, + { + let mut guard = Guard { slice: this, initialized: 0 }; + + for element in guard.slice.iter_mut() { + element.write(f()); + guard.initialized += 1; + } + + super::forget(guard); + + // SAFETY: Valid elements have just been written into `this` so it is initialized + unsafe { this.assume_init_mut() } + } + + /// Fills a slice with elements yielded by an iterator until either all elements have been + /// initialized or the iterator is empty. + /// + /// Returns two slices. The first slice contains the initialized portion of the original slice. + /// The second slice is the still-uninitialized remainder of the original slice. + /// + /// # Panics + /// + /// This function panics if the iterator's `next` function panics. + /// + /// If such a panic occurs, any elements previously initialized during this operation will be + /// dropped. + /// + /// # Examples + /// + /// Completely filling the slice: + /// + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = [const { MaybeUninit::uninit() }; 5]; + /// + /// let iter = [1, 2, 3].into_iter().cycle(); + /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); + /// + /// assert_eq!(initialized, &mut [1, 2, 3, 1, 2]); + /// assert_eq!(remainder.len(), 0); + /// ``` + /// + /// Partially filling the slice: + /// + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = [const { MaybeUninit::uninit() }; 5]; + /// let iter = [1, 2]; + /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); + /// + /// assert_eq!(initialized, &mut [1, 2]); + /// assert_eq!(remainder.len(), 3); + /// ``` + /// + /// Checking an iterator after filling a slice: + /// + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = [const { MaybeUninit::uninit() }; 3]; + /// let mut iter = [1, 2, 3, 4, 5].into_iter(); + /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter.by_ref()); + /// + /// assert_eq!(initialized, &mut [1, 2, 3]); + /// assert_eq!(remainder.len(), 0); + /// assert_eq!(iter.as_slice(), &[4, 5]); + /// ``` + #[unstable(feature = "maybe_uninit_fill", issue = "117428")] + pub fn fill_from(this: &mut [MaybeUninit], it: I) -> (&mut [T], &mut [MaybeUninit]) + where + I: IntoIterator, + { + let iter = it.into_iter(); + let mut guard = Guard { slice: this, initialized: 0 }; + + for (element, val) in guard.slice.iter_mut().zip(iter) { + element.write(val); + guard.initialized += 1; + } + + let initialized_len = guard.initialized; + super::forget(guard); + + // SAFETY: guard.initialized <= this.len() + let (initted, remainder) = unsafe { this.split_at_mut_unchecked(initialized_len) }; + + // SAFETY: Valid elements have just been written into `init`, so that portion + // of `this` is initialized. + (unsafe { initted.assume_init_mut() }, remainder) + } + + /// Deprecated version of [`slice::as_bytes`]. + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + #[deprecated( + note = "replaced by inherent as_bytes method; will eventually be removed", + since = "1.83.0" + )] + pub fn slice_as_bytes(this: &[MaybeUninit]) -> &[MaybeUninit] { + this.as_bytes() + } + + /// Deprecated version of [`slice::as_bytes_mut`]. + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + #[deprecated( + note = "replaced by inherent as_bytes_mut method; will eventually be removed", + since = "1.83.0" + )] + pub fn slice_as_bytes_mut(this: &mut [MaybeUninit]) -> &mut [MaybeUninit] { + this.as_bytes_mut() + } +} + +impl [MaybeUninit] { + /// Copies the elements from `src` to `self`, + /// returning a mutable reference to the now initialized contents of `self`. + /// + /// If `T` does not implement `Copy`, use [`write_clone_of_slice`] instead. + /// + /// This is similar to [`slice::copy_from_slice`]. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// use std::mem::MaybeUninit; + /// + /// let mut dst = [MaybeUninit::uninit(); 32]; + /// let src = [0; 32]; + /// + /// let init = dst.write_copy_of_slice(&src); + /// + /// assert_eq!(init, src); + /// ``` + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// + /// let mut vec = Vec::with_capacity(32); + /// let src = [0; 16]; + /// + /// vec.spare_capacity_mut()[..src.len()].write_copy_of_slice(&src); + /// + /// // SAFETY: we have just copied all the elements of len into the spare capacity + /// // the first src.len() elements of the vec are valid now. + /// unsafe { + /// vec.set_len(src.len()); + /// } + /// + /// assert_eq!(vec, src); + /// ``` + /// + /// [`write_clone_of_slice`]: slice::write_clone_of_slice + #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[rustc_const_unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + pub const fn write_copy_of_slice(&mut self, src: &[T]) -> &mut [T] + where + T: Copy, + { + // SAFETY: &[T] and &[MaybeUninit] have the same layout + let uninit_src: &[MaybeUninit] = unsafe { super::transmute(src) }; + + self.copy_from_slice(uninit_src); + + // SAFETY: Valid elements have just been copied into `self` so it is initialized + unsafe { self.assume_init_mut() } + } + + /// Clones the elements from `src` to `self`, + /// returning a mutable reference to the now initialized contents of `self`. + /// Any already initialized elements will not be dropped. + /// + /// If `T` implements `Copy`, use [`write_copy_of_slice`] instead. + /// + /// This is similar to [`slice::clone_from_slice`] but does not drop existing elements. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths, or if the implementation of `Clone` panics. + /// + /// If there is a panic, the already cloned elements will be dropped. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// use std::mem::MaybeUninit; + /// + /// let mut dst = [const { MaybeUninit::uninit() }; 5]; + /// let src = ["wibbly", "wobbly", "timey", "wimey", "stuff"].map(|s| s.to_string()); + /// + /// let init = dst.write_clone_of_slice(&src); + /// + /// assert_eq!(init, src); + /// + /// # // Prevent leaks for Miri + /// # unsafe { std::ptr::drop_in_place(init); } + /// ``` + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// + /// let mut vec = Vec::with_capacity(32); + /// let src = ["rust", "is", "a", "pretty", "cool", "language"].map(|s| s.to_string()); + /// + /// vec.spare_capacity_mut()[..src.len()].write_clone_of_slice(&src); + /// + /// // SAFETY: we have just cloned all the elements of len into the spare capacity + /// // the first src.len() elements of the vec are valid now. + /// unsafe { + /// vec.set_len(src.len()); + /// } + /// + /// assert_eq!(vec, src); + /// ``` + /// + /// [`write_copy_of_slice`]: slice::write_copy_of_slice + #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + pub fn write_clone_of_slice(&mut self, src: &[T]) -> &mut [T] + where + T: Clone, + { + // unlike copy_from_slice this does not call clone_from_slice on the slice + // this is because `MaybeUninit` does not implement Clone. + + assert_eq!(self.len(), src.len(), "destination and source slices have different lengths"); + + // NOTE: We need to explicitly slice them to the same length + // for bounds checking to be elided, and the optimizer will + // generate memcpy for simple cases (for example T = u8). + let len = self.len(); + let src = &src[..len]; + + // guard is needed b/c panic might happen during a clone + let mut guard = Guard { slice: self, initialized: 0 }; + + for i in 0..len { + guard.slice[i].write(src[i].clone()); + guard.initialized += 1; + } + + super::forget(guard); + + // SAFETY: Valid elements have just been written into `self` so it is initialized + unsafe { self.assume_init_mut() } + } + + /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. /// /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still /// contain padding bytes which are left uninitialized. @@ -1387,21 +1432,22 @@ impl MaybeUninit { /// use std::mem::MaybeUninit; /// /// let uninit = [MaybeUninit::new(0x1234u16), MaybeUninit::new(0x5678u16)]; - /// let uninit_bytes = MaybeUninit::slice_as_bytes(&uninit); - /// let bytes = unsafe { MaybeUninit::slice_assume_init_ref(&uninit_bytes) }; + /// let uninit_bytes = uninit.as_bytes(); + /// let bytes = unsafe { uninit_bytes.assume_init_ref() }; /// let val1 = u16::from_ne_bytes(bytes[0..2].try_into().unwrap()); /// let val2 = u16::from_ne_bytes(bytes[2..4].try_into().unwrap()); /// assert_eq!(&[val1, val2], &[0x1234u16, 0x5678u16]); /// ``` #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub fn slice_as_bytes(this: &[MaybeUninit]) -> &[MaybeUninit] { - let bytes = mem::size_of_val(this); + pub const fn as_bytes(&self) -> &[MaybeUninit] { // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { slice::from_raw_parts(this.as_ptr() as *const MaybeUninit, bytes) } + unsafe { + slice::from_raw_parts(self.as_ptr().cast::>(), super::size_of_val(self)) + } } - /// Returns the contents of this mutable slice of `MaybeUninit` as a mutable slice of - /// potentially uninitialized bytes. + /// Returns the contents of this `MaybeUninit` slice as a mutable slice of potentially + /// uninitialized bytes. /// /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still /// contain padding bytes which are left uninitialized. @@ -1414,8 +1460,8 @@ impl MaybeUninit { /// /// let mut uninit = [MaybeUninit::::uninit(), MaybeUninit::::uninit()]; /// let uninit_bytes = MaybeUninit::slice_as_bytes_mut(&mut uninit); - /// MaybeUninit::copy_from_slice(uninit_bytes, &[0x12, 0x34, 0x56, 0x78]); - /// let vals = unsafe { MaybeUninit::slice_assume_init_ref(&uninit) }; + /// uninit_bytes.write_copy_of_slice(&[0x12, 0x34, 0x56, 0x78]); + /// let vals = unsafe { uninit.assume_init_ref() }; /// if cfg!(target_endian = "little") { /// assert_eq!(vals, &[0x3412u16, 0x7856u16]); /// } else { @@ -1423,10 +1469,74 @@ impl MaybeUninit { /// } /// ``` #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub fn slice_as_bytes_mut(this: &mut [MaybeUninit]) -> &mut [MaybeUninit] { - let bytes = mem::size_of_val(this); + pub const fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { slice::from_raw_parts_mut(this.as_mut_ptr() as *mut MaybeUninit, bytes) } + unsafe { + slice::from_raw_parts_mut( + self.as_mut_ptr() as *mut MaybeUninit, + super::size_of_val(self), + ) + } + } + + /// Drops the contained values in place. + /// + /// # Safety + /// + /// It is up to the caller to guarantee that every `MaybeUninit` in the slice + /// really is in an initialized state. Calling this when the content is not yet + /// fully initialized causes undefined behavior. + /// + /// On top of that, all additional invariants of the type `T` must be + /// satisfied, as the `Drop` implementation of `T` (or its members) may + /// rely on this. For example, setting a `Vec` to an invalid but + /// non-null address makes it initialized (under the current implementation; + /// this does not constitute a stable guarantee), because the only + /// requirement the compiler knows about it is that the data pointer must be + /// non-null. Dropping such a `Vec` however will cause undefined + /// behaviour. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[inline(always)] + pub unsafe fn assume_init_drop(&mut self) { + if !self.is_empty() { + // SAFETY: the caller must guarantee that every element of `self` + // is initialized and satisfies all invariants of `T`. + // Dropping the value in place is safe if that is the case. + unsafe { ptr::drop_in_place(self as *mut [MaybeUninit] as *mut [T]) } + } + } + + /// Gets a shared reference to the contained value. + /// + /// # Safety + /// + /// Calling this when the content is not yet fully initialized causes undefined + /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in + /// the slice really is in an initialized state. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[inline(always)] + pub const unsafe fn assume_init_ref(&self) -> &[T] { + // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that + // `slice` is initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`. + // The pointer obtained is valid since it refers to memory owned by `slice` which is a + // reference and thus guaranteed to be valid for reads. + unsafe { &*(self as *const Self as *const [T]) } + } + + /// Gets a mutable (unique) reference to the contained value. + /// + /// # Safety + /// + /// Calling this when the content is not yet fully initialized causes undefined + /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in the + /// slice really is in an initialized state. For instance, `.assume_init_mut()` cannot + /// be used to initialize a `MaybeUninit` slice. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[inline(always)] + pub const unsafe fn assume_init_mut(&mut self) -> &mut [T] { + // SAFETY: similar to safety notes for `slice_get_ref`, but we have a + // mutable reference which is also guaranteed to be valid for writes. + unsafe { &mut *(self as *mut Self as *mut [T]) } } } @@ -1479,7 +1589,7 @@ impl<'a, T> Drop for Guard<'a, T> { let initialized_part = &mut self.slice[..self.initialized]; // SAFETY: this raw sub-slice will contain only initialized objects. unsafe { - crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part)); + initialized_part.assume_init_drop(); } } } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 2d66e5c2f2a7..b9bb6d6a13f7 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -333,7 +333,7 @@ pub const fn size_of() -> usize { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_size_of_val", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_size_of_val", since = "1.85.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "mem_size_of_val")] pub const fn size_of_val(val: &T) -> usize { // SAFETY: `val` is a reference, so it's a valid raw pointer @@ -484,7 +484,7 @@ pub const fn align_of() -> usize { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_align_of_val", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_align_of_val", since = "1.85.0")] #[allow(deprecated)] pub const fn align_of_val(val: &T) -> usize { // SAFETY: val is a reference, so it's a valid raw pointer @@ -725,7 +725,7 @@ pub unsafe fn uninitialized() -> T { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_swap", since = "1.85.0")] #[rustc_diagnostic_item = "mem_swap"] pub const fn swap(x: &mut T, y: &mut T) { // SAFETY: `&mut` guarantees these are typed readable and writable diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index 9cf587650d94..6a4f84c849cb 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -84,9 +84,8 @@ use crate::marker::{ConstParamTy_, UnsizedConstParamTy}; /// `usize` is stable, but not portable. #[unstable(feature = "transmutability", issue = "99571")] #[lang = "transmute_trait"] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] #[rustc_coinductive] pub unsafe trait TransmuteFrom where diff --git a/library/core/src/net/display_buffer.rs b/library/core/src/net/display_buffer.rs index bab84a97308b..a7d12217081f 100644 --- a/library/core/src/net/display_buffer.rs +++ b/library/core/src/net/display_buffer.rs @@ -18,7 +18,7 @@ impl DisplayBuffer { // SAFETY: `buf` is only written to by the `fmt::Write::write_str` implementation // which writes a valid UTF-8 string to `buf` and correctly sets `len`. unsafe { - let s = MaybeUninit::slice_assume_init_ref(&self.buf[..self.len]); + let s = self.buf[..self.len].assume_init_ref(); str::from_utf8_unchecked(s) } } @@ -29,7 +29,7 @@ impl fmt::Write for DisplayBuffer { let bytes = s.as_bytes(); if let Some(buf) = self.buf.get_mut(self.len..(self.len + bytes.len())) { - MaybeUninit::copy_from_slice(buf, bytes); + buf.write_copy_of_slice(bytes); self.len += bytes.len(); Ok(()) } else { diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 4c0d95f95e56..2b6adef65e94 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -818,7 +818,7 @@ impl f32 { /// ``` #[must_use = "this returns the result of the operation, without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn recip(self) -> f32 { 1.0 / self @@ -836,7 +836,7 @@ impl f32 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn to_degrees(self) -> f32 { // Use a constant for better precision. @@ -856,7 +856,7 @@ impl f32 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn to_radians(self) -> f32 { const RADS_PER_DEG: f32 = consts::PI / 180.0; @@ -878,7 +878,7 @@ impl f32 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn max(self, other: f32) -> f32 { intrinsics::maxnumf32(self, other) @@ -899,7 +899,7 @@ impl f32 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn min(self, other: f32) -> f32 { intrinsics::minnumf32(self, other) @@ -988,8 +988,8 @@ impl f32 { /// assert_eq!((-5.5f32).midpoint(8.0), 1.25); /// ``` #[inline] - #[stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] pub const fn midpoint(self, other: f32) -> f32 { cfg_if! { // Allow faster implementation that have known good 64-bit float @@ -1396,7 +1396,7 @@ impl f32 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "clamp", since = "1.50.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn clamp(mut self, min: f32, max: f32) -> f32 { const_assert!( @@ -1433,7 +1433,7 @@ impl f32 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn abs(self) -> f32 { // SAFETY: this is actually a safe intrinsic @@ -1458,7 +1458,7 @@ impl f32 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn signum(self) -> f32 { if self.is_nan() { Self::NAN } else { 1.0_f32.copysign(self) } @@ -1493,7 +1493,7 @@ impl f32 { #[must_use = "method returns a new number and does not mutate the original value"] #[inline] #[stable(feature = "copysign", since = "1.35.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] pub const fn copysign(self, sign: f32) -> f32 { // SAFETY: this is actually a safe intrinsic unsafe { intrinsics::copysignf32(self, sign) } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 77ca56df0670..037b3afa6c46 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -835,7 +835,7 @@ impl f64 { /// ``` #[must_use = "this returns the result of the operation, without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn recip(self) -> f64 { 1.0 / self @@ -853,7 +853,7 @@ impl f64 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn to_degrees(self) -> f64 { // The division here is correctly rounded with respect to the true @@ -874,7 +874,7 @@ impl f64 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn to_radians(self) -> f64 { const RADS_PER_DEG: f64 = consts::PI / 180.0; @@ -896,7 +896,7 @@ impl f64 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn max(self, other: f64) -> f64 { intrinsics::maxnumf64(self, other) @@ -917,7 +917,7 @@ impl f64 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn min(self, other: f64) -> f64 { intrinsics::minnumf64(self, other) @@ -1006,8 +1006,8 @@ impl f64 { /// assert_eq!((-5.5f64).midpoint(8.0), 1.25); /// ``` #[inline] - #[stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] pub const fn midpoint(self, other: f64) -> f64 { const LO: f64 = f64::MIN_POSITIVE * 2.; const HI: f64 = f64::MAX / 2.; @@ -1396,7 +1396,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "clamp", since = "1.50.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn clamp(mut self, min: f64, max: f64) -> f64 { const_assert!( @@ -1433,7 +1433,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn abs(self) -> f64 { // SAFETY: this is actually a safe intrinsic @@ -1458,7 +1458,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn signum(self) -> f64 { if self.is_nan() { Self::NAN } else { 1.0_f64.copysign(self) } @@ -1492,7 +1492,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "copysign", since = "1.35.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn copysign(self, sign: f64) -> f64 { // SAFETY: this is actually a safe intrinsic diff --git a/library/core/src/num/flt2dec/mod.rs b/library/core/src/num/flt2dec/mod.rs index d6413fadc338..7601e3e2c58a 100644 --- a/library/core/src/num/flt2dec/mod.rs +++ b/library/core/src/num/flt2dec/mod.rs @@ -210,10 +210,10 @@ fn digits_to_dec_str<'a>( if frac_digits > buf.len() && frac_digits - buf.len() > minus_exp { parts[3] = MaybeUninit::new(Part::Zero((frac_digits - buf.len()) - minus_exp)); // SAFETY: we just initialized the elements `..4`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } + unsafe { parts[..4].assume_init_ref() } } else { // SAFETY: we just initialized the elements `..3`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) } + unsafe { parts[..3].assume_init_ref() } } } else { let exp = exp as usize; @@ -225,10 +225,10 @@ fn digits_to_dec_str<'a>( if frac_digits > buf.len() - exp { parts[3] = MaybeUninit::new(Part::Zero(frac_digits - (buf.len() - exp))); // SAFETY: we just initialized the elements `..4`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } + unsafe { parts[..4].assume_init_ref() } } else { // SAFETY: we just initialized the elements `..3`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) } + unsafe { parts[..3].assume_init_ref() } } } else { // the decimal point is after rendered digits: [1234][____0000] or [1234][__][.][__]. @@ -238,10 +238,10 @@ fn digits_to_dec_str<'a>( parts[2] = MaybeUninit::new(Part::Copy(b".")); parts[3] = MaybeUninit::new(Part::Zero(frac_digits)); // SAFETY: we just initialized the elements `..4`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } + unsafe { parts[..4].assume_init_ref() } } else { // SAFETY: we just initialized the elements `..2`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) } + unsafe { parts[..2].assume_init_ref() } } } } @@ -292,7 +292,7 @@ fn digits_to_exp_str<'a>( parts[n + 1] = MaybeUninit::new(Part::Num(exp as u16)); } // SAFETY: we just initialized the elements `..n + 2`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..n + 2]) } + unsafe { parts[..n + 2].assume_init_ref() } } /// Sign formatting options. @@ -366,12 +366,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Zero => { if frac_digits > 0 { @@ -381,14 +381,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..2`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, + parts: unsafe { parts[..2].assume_init_ref() }, } } else { parts[0] = MaybeUninit::new(Part::Copy(b"0")); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + parts: unsafe { parts[..1].assume_init_ref() }, } } } @@ -442,12 +442,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Zero => { parts[0] = if dec_bounds.0 <= 0 && 0 < dec_bounds.1 { @@ -456,7 +456,7 @@ where MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" })) }; // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Finite(ref decoded) => { let (buf, exp) = format_shortest(decoded, buf); @@ -533,12 +533,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Zero => { if ndigits > 1 { @@ -549,14 +549,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..3`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) }, + parts: unsafe { parts[..3].assume_init_ref() }, } } else { parts[0] = MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" })); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + parts: unsafe { parts[..1].assume_init_ref() }, } } } @@ -607,12 +607,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Zero => { if frac_digits > 0 { @@ -622,14 +622,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..2`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, + parts: unsafe { parts[..2].assume_init_ref() }, } } else { parts[0] = MaybeUninit::new(Part::Copy(b"0")); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + parts: unsafe { parts[..1].assume_init_ref() }, } } } @@ -654,14 +654,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..2`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, + parts: unsafe { parts[..2].assume_init_ref() }, } } else { parts[0] = MaybeUninit::new(Part::Copy(b"0")); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + parts: unsafe { parts[..1].assume_init_ref() }, } } } else { diff --git a/library/core/src/num/flt2dec/strategy/dragon.rs b/library/core/src/num/flt2dec/strategy/dragon.rs index e801f07b3bc0..dd73e4b4846d 100644 --- a/library/core/src/num/flt2dec/strategy/dragon.rs +++ b/library/core/src/num/flt2dec/strategy/dragon.rs @@ -247,7 +247,7 @@ pub fn format_shortest<'a>( // it seems that this condition is very hard to satisfy (possibly impossible), // but we are just being safe and consistent here. // SAFETY: we initialized that memory above. - if let Some(c) = round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }) { + if let Some(c) = round_up(unsafe { buf[..i].assume_init_mut() }) { buf[i] = MaybeUninit::new(c); i += 1; k += 1; @@ -255,7 +255,7 @@ pub fn format_shortest<'a>( } // SAFETY: we initialized that memory above. - (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..i]) }, k) + (unsafe { buf[..i].assume_init_ref() }, k) } /// The exact and fixed mode implementation for Dragon. @@ -333,7 +333,7 @@ pub fn format_exact<'a>( *c = MaybeUninit::new(b'0'); } // SAFETY: we initialized that memory above. - return (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, k); + return (unsafe { buf[..len].assume_init_ref() }, k); } let mut d = 0; @@ -372,7 +372,7 @@ pub fn format_exact<'a>( // if rounding up changes the length, the exponent should also change. // but we've been requested a fixed number of digits, so do not alter the buffer... // SAFETY: we initialized that memory above. - if let Some(c) = round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..len]) }) { + if let Some(c) = round_up(unsafe { buf[..len].assume_init_mut() }) { // ...unless we've been requested the fixed precision instead. // we also need to check that, if the original buffer was empty, // the additional digit can only be added when `k == limit` (edge case). @@ -385,5 +385,5 @@ pub fn format_exact<'a>( } // SAFETY: we initialized that memory above. - (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, k) + (unsafe { buf[..len].assume_init_ref() }, k) } diff --git a/library/core/src/num/flt2dec/strategy/grisu.rs b/library/core/src/num/flt2dec/strategy/grisu.rs index bdf544a4133b..2816de4c6333 100644 --- a/library/core/src/num/flt2dec/strategy/grisu.rs +++ b/library/core/src/num/flt2dec/strategy/grisu.rs @@ -275,7 +275,7 @@ pub fn format_shortest_opt<'a>( let ten_kappa = (ten_kappa as u64) << e; // scale 10^kappa back to the shared exponent return round_and_weed( // SAFETY: we initialized that memory above. - unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }, + unsafe { buf[..i].assume_init_mut() }, exp, plus1rem, delta1, @@ -324,7 +324,7 @@ pub fn format_shortest_opt<'a>( let ten_kappa = 1 << e; // implicit divisor return round_and_weed( // SAFETY: we initialized that memory above. - unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }, + unsafe { buf[..i].assume_init_mut() }, exp, r, threshold, @@ -713,7 +713,7 @@ pub fn format_exact_opt<'a>( // `10^kappa` did not overflow after all, the second check is fine. if ten_kappa - remainder > remainder && ten_kappa - 2 * remainder >= 2 * ulp { // SAFETY: our caller initialized that memory. - return Some((unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, exp)); + return Some((unsafe { buf[..len].assume_init_ref() }, exp)); } // :<------- remainder ------>| : @@ -736,7 +736,7 @@ pub fn format_exact_opt<'a>( if remainder > ulp && ten_kappa - (remainder - ulp) <= remainder - ulp { if let Some(c) = // SAFETY: our caller must have initialized that memory. - round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..len]) }) + round_up(unsafe { buf[..len].assume_init_mut() }) { // only add an additional digit when we've been requested the fixed precision. // we also need to check that, if the original buffer was empty, @@ -748,7 +748,7 @@ pub fn format_exact_opt<'a>( } } // SAFETY: we and our caller initialized that memory. - return Some((unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, exp)); + return Some((unsafe { buf[..len].assume_init_ref() }, exp)); } // otherwise we are doomed (i.e., some values between `v - 1 ulp` and `v + 1 ulp` are diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 1f5b8ce6c6e2..6c1b568e231d 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -51,6 +51,10 @@ mod overflow_panic; mod saturating; mod wrapping; +/// 100% perma-unstable +#[doc(hidden)] +pub mod niche_types; + #[stable(feature = "rust1", since = "1.0.0")] #[cfg(not(no_fp_fmt_parse))] pub use dec2flt::ParseFloatError; @@ -138,8 +142,8 @@ macro_rules! midpoint_impl { #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")] #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".midpoint(4), 2);")] /// ``` - #[stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -192,8 +196,8 @@ macro_rules! midpoint_impl { #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")] #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".midpoint(4), 2);")] /// ``` - #[stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/num/niche_types.rs b/library/core/src/num/niche_types.rs new file mode 100644 index 000000000000..096713c318f8 --- /dev/null +++ b/library/core/src/num/niche_types.rs @@ -0,0 +1,168 @@ +#![unstable( + feature = "temporary_niche_types", + issue = "none", + reason = "for core, alloc, and std internals until pattern types are further along" +)] + +use crate::cmp::Ordering; +use crate::fmt; +use crate::hash::{Hash, Hasher}; +use crate::marker::StructuralPartialEq; + +macro_rules! define_valid_range_type { + ($( + $(#[$m:meta])* + $vis:vis struct $name:ident($int:ident as $uint:ident in $low:literal..=$high:literal); + )+) => {$( + #[derive(Clone, Copy, Eq)] + #[repr(transparent)] + #[rustc_layout_scalar_valid_range_start($low)] + #[rustc_layout_scalar_valid_range_end($high)] + $(#[$m])* + $vis struct $name($int); + + const _: () = { + // With the `valid_range` attributes, it's always specified as unsigned + assert!(<$uint>::MIN == 0); + let ulow: $uint = $low; + let uhigh: $uint = $high; + assert!(ulow <= uhigh); + + assert!(size_of::<$int>() == size_of::<$uint>()); + }; + + impl $name { + /// Constructs an instance of this type from the underlying integer + /// primitive without checking whether its zero. + /// + /// # Safety + /// Immediate language UB if `val == 0`, as it violates the validity + /// invariant of this type. + #[inline] + pub const unsafe fn new_unchecked(val: $int) -> Self { + // SAFETY: Caller promised that `val` is non-zero. + unsafe { $name(val) } + } + + #[inline] + pub const fn as_inner(self) -> $int { + // SAFETY: This is a transparent wrapper, so unwrapping it is sound + // (Not using `.0` due to MCP#807.) + unsafe { crate::mem::transmute(self) } + } + } + + // This is required to allow matching a constant. We don't get it from a derive + // because the derived `PartialEq` would do a field projection, which is banned + // by . + impl StructuralPartialEq for $name {} + + impl PartialEq for $name { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_inner() == other.as_inner() + } + } + + impl Ord for $name { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + Ord::cmp(&self.as_inner(), &other.as_inner()) + } + } + + impl PartialOrd for $name { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(Ord::cmp(self, other)) + } + } + + impl Hash for $name { + // Required method + fn hash(&self, state: &mut H) { + Hash::hash(&self.as_inner(), state); + } + } + + impl fmt::Debug for $name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + <$int as fmt::Debug>::fmt(&self.as_inner(), f) + } + } + )+}; +} + +define_valid_range_type! { + pub struct Nanoseconds(u32 as u32 in 0..=999_999_999); +} + +impl Nanoseconds { + // SAFETY: 0 is within the valid range + pub const ZERO: Self = unsafe { Nanoseconds::new_unchecked(0) }; +} + +impl Default for Nanoseconds { + #[inline] + fn default() -> Self { + Self::ZERO + } +} + +define_valid_range_type! { + pub struct NonZeroU8Inner(u8 as u8 in 1..=0xff); + pub struct NonZeroU16Inner(u16 as u16 in 1..=0xff_ff); + pub struct NonZeroU32Inner(u32 as u32 in 1..=0xffff_ffff); + pub struct NonZeroU64Inner(u64 as u64 in 1..=0xffffffff_ffffffff); + pub struct NonZeroU128Inner(u128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); + + pub struct NonZeroI8Inner(i8 as u8 in 1..=0xff); + pub struct NonZeroI16Inner(i16 as u16 in 1..=0xff_ff); + pub struct NonZeroI32Inner(i32 as u32 in 1..=0xffff_ffff); + pub struct NonZeroI64Inner(i64 as u64 in 1..=0xffffffff_ffffffff); + pub struct NonZeroI128Inner(i128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); +} + +#[cfg(target_pointer_width = "16")] +define_valid_range_type! { + pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff); + pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff); + pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff); +} +#[cfg(target_pointer_width = "32")] +define_valid_range_type! { + pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff); + pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff); + pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff); +} +#[cfg(target_pointer_width = "64")] +define_valid_range_type! { + pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff_ffff_ffff); + pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff_ffff_ffff); + pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff_ffff_ffff); +} + +define_valid_range_type! { + pub struct U32NotAllOnes(u32 as u32 in 0..=0xffff_fffe); + pub struct I32NotAllOnes(i32 as u32 in 0..=0xffff_fffe); + + pub struct U64NotAllOnes(u64 as u64 in 0..=0xffff_ffff_ffff_fffe); + pub struct I64NotAllOnes(i64 as u64 in 0..=0xffff_ffff_ffff_fffe); +} + +pub trait NotAllOnesHelper { + type Type; +} +pub type NotAllOnes = ::Type; +impl NotAllOnesHelper for u32 { + type Type = U32NotAllOnes; +} +impl NotAllOnesHelper for i32 { + type Type = I32NotAllOnes; +} +impl NotAllOnesHelper for u64 { + type Type = U64NotAllOnes; +} +impl NotAllOnesHelper for i64 { + type Type = I64NotAllOnes; +} diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index a9294306b1b6..dbce64420ac4 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -37,41 +37,12 @@ pub unsafe trait ZeroablePrimitive: Sized + Copy + private::Sealed { macro_rules! impl_zeroable_primitive { ($($NonZeroInner:ident ( $primitive:ty )),+ $(,)?) => { mod private { - use super::*; - #[unstable( feature = "nonzero_internals", reason = "implementation detail which may disappear or be replaced at any time", issue = "none" )] pub trait Sealed {} - - $( - // This inner type is never shown directly, so intentionally does not have Debug - #[expect(missing_debug_implementations)] - // Since this struct is non-generic and derives Copy, - // the derived Clone is `*self` and thus doesn't field-project. - #[derive(Clone, Copy)] - #[repr(transparent)] - #[rustc_layout_scalar_valid_range_start(1)] - #[rustc_nonnull_optimization_guaranteed] - #[unstable( - feature = "nonzero_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" - )] - pub struct $NonZeroInner($primitive); - - // This is required to allow matching a constant. We don't get it from a derive - // because the derived `PartialEq` would do a field projection, which is banned - // by . - #[unstable( - feature = "nonzero_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" - )] - impl StructuralPartialEq for $NonZeroInner {} - )+ } $( @@ -88,7 +59,7 @@ macro_rules! impl_zeroable_primitive { issue = "none" )] unsafe impl ZeroablePrimitive for $primitive { - type NonZeroInner = private::$NonZeroInner; + type NonZeroInner = super::niche_types::$NonZeroInner; } )+ }; @@ -1543,8 +1514,8 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// # Some(()) /// # } /// ``` - #[stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index 565bccf58982..810b906b8715 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -73,7 +73,7 @@ append_const_msg )] #[doc(alias = "+")] -#[cfg_attr(not(bootstrap), const_trait)] +#[const_trait] pub trait Add { /// The resulting type after applying the `+` operator. #[stable(feature = "rust1", since = "1.0.0")] @@ -95,18 +95,6 @@ pub trait Add { macro_rules! add_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(bootstrap)] - impl Add for $t { - type Output = $t; - - #[inline] - #[track_caller] - #[rustc_inherit_overflow_checks] - fn add(self, other: $t) -> $t { self + other } - } - - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(bootstrap))] impl const Add for $t { type Output = $t; diff --git a/library/core/src/ops/async_function.rs b/library/core/src/ops/async_function.rs index 0073afd49607..c90ae7babbd9 100644 --- a/library/core/src/ops/async_function.rs +++ b/library/core/src/ops/async_function.rs @@ -4,8 +4,7 @@ use crate::marker::Tuple; /// An async-aware version of the [`Fn`](crate::ops::Fn) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] #[rustc_paren_sugar] #[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] @@ -19,8 +18,7 @@ pub trait AsyncFn: AsyncFnMut { /// An async-aware version of the [`FnMut`](crate::ops::FnMut) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] #[rustc_paren_sugar] #[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] @@ -41,8 +39,7 @@ pub trait AsyncFnMut: AsyncFnOnce { /// An async-aware version of the [`FnOnce`](crate::ops::FnOnce) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] #[rustc_paren_sugar] #[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] @@ -67,8 +64,7 @@ mod impls { use super::{AsyncFn, AsyncFnMut, AsyncFnOnce}; use crate::marker::Tuple; - #[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] - #[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] + #[stable(feature = "async_closure", since = "1.85.0")] impl AsyncFn for &F where F: AsyncFn, @@ -78,8 +74,7 @@ mod impls { } } - #[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] - #[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] + #[stable(feature = "async_closure", since = "1.85.0")] impl AsyncFnMut for &F where F: AsyncFn, @@ -94,8 +89,7 @@ mod impls { } } - #[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] - #[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] + #[stable(feature = "async_closure", since = "1.85.0")] impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce for &'a F where F: AsyncFn, @@ -108,8 +102,7 @@ mod impls { } } - #[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] - #[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] + #[stable(feature = "async_closure", since = "1.85.0")] impl AsyncFnMut for &mut F where F: AsyncFnMut, @@ -124,8 +117,7 @@ mod impls { } } - #[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] - #[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] + #[stable(feature = "async_closure", since = "1.85.0")] impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce for &'a mut F where F: AsyncFnMut, diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index c36d55fe2a8c..ed0d30a0f502 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -133,7 +133,7 @@ #[doc(alias = "&*")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Deref"] -#[cfg_attr(not(bootstrap), const_trait)] +#[const_trait] pub trait Deref { /// The resulting type after dereferencing. #[stable(feature = "rust1", since = "1.0.0")] @@ -148,18 +148,6 @@ pub trait Deref { fn deref(&self) -> &Self::Target; } -#[cfg(bootstrap)] -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for &T { - type Target = T; - - #[rustc_diagnostic_item = "noop_method_deref"] - fn deref(&self) -> &T { - *self - } -} - -#[cfg(not(bootstrap))] #[stable(feature = "rust1", since = "1.0.0")] impl const Deref for &T { type Target = T; @@ -173,17 +161,6 @@ impl const Deref for &T { #[stable(feature = "rust1", since = "1.0.0")] impl !DerefMut for &T {} -#[cfg(bootstrap)] -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for &mut T { - type Target = T; - - fn deref(&self) -> &T { - *self - } -} - -#[cfg(not(bootstrap))] #[stable(feature = "rust1", since = "1.0.0")] impl const Deref for &mut T { type Target = T; @@ -282,7 +259,6 @@ impl const Deref for &mut T { /// *x = 'b'; /// assert_eq!('b', x.value); /// ``` -#[cfg(not(bootstrap))] #[lang = "deref_mut"] #[doc(alias = "*")] #[stable(feature = "rust1", since = "1.0.0")] @@ -294,27 +270,6 @@ pub trait DerefMut: ~const Deref { fn deref_mut(&mut self) -> &mut Self::Target; } -/// Bootstrap -#[lang = "deref_mut"] -#[doc(alias = "*")] -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg(bootstrap)] -pub trait DerefMut: Deref { - /// Mutably dereferences the value. - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_diagnostic_item = "deref_mut_method"] - fn deref_mut(&mut self) -> &mut Self::Target; -} - -#[cfg(bootstrap)] -#[stable(feature = "rust1", since = "1.0.0")] -impl DerefMut for &mut T { - fn deref_mut(&mut self) -> &mut T { - *self - } -} - -#[cfg(not(bootstrap))] #[stable(feature = "rust1", since = "1.0.0")] impl const DerefMut for &mut T { fn deref_mut(&mut self) -> &mut T { diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs index f3314364e542..78b5252195f8 100644 --- a/library/core/src/ops/drop.rs +++ b/library/core/src/ops/drop.rs @@ -203,7 +203,7 @@ /// [nomicon]: ../../nomicon/phantom-data.html#an-exception-the-special-case-of-the-standard-library-and-its-unstable-may_dangle #[lang = "drop"] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(bootstrap), const_trait)] +#[const_trait] pub trait Drop { /// Executes the destructor for this type. /// diff --git a/library/core/src/prelude/mod.rs b/library/core/src/prelude/mod.rs index 9b23874bcf7b..d3fda1cd273f 100644 --- a/library/core/src/prelude/mod.rs +++ b/library/core/src/prelude/mod.rs @@ -71,7 +71,7 @@ pub mod rust_2021 { /// The 2024 version of the core prelude. /// /// See the [module-level documentation](self) for more. -#[stable(feature = "prelude_2024", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "prelude_2024", since = "1.85.0")] pub mod rust_2024 { #[stable(feature = "rust1", since = "1.0.0")] pub use super::common::*; @@ -84,7 +84,7 @@ pub mod rust_2024 { #[doc(no_inline)] pub use crate::convert::{TryFrom, TryInto}; - #[stable(feature = "prelude_2024", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "prelude_2024", since = "1.85.0")] #[doc(no_inline)] pub use crate::future::{Future, IntoFuture}; } diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index b1d5e1fa3a05..e93b5658e243 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -53,9 +53,8 @@ use crate::ptr::NonNull; /// /// [`to_raw_parts`]: *const::to_raw_parts #[lang = "pointee_trait"] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait Pointee { /// The type for metadata in pointers and references to `Self`. #[lang = "metadata_type"] diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index a70af793004b..f58c0e124114 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -777,7 +777,7 @@ pub fn with_exposed_provenance_mut(addr: usize) -> *mut T { /// # type T = i32; /// # fn foo() -> T { 42 } /// // The temporary holding the return value of `foo` does *not* have its lifetime extended, -/// // because the surrounding expression involves no function call. +/// // because the surrounding expression involves a function call. /// let p = ptr::from_ref(&foo()); /// unsafe { p.read() }; // UB! Reading from a dangling pointer ⚠️ /// ``` @@ -828,7 +828,7 @@ pub const fn from_ref(r: &T) -> *const T { /// # type T = i32; /// # fn foo() -> T { 42 } /// // The temporary holding the return value of `foo` does *not* have its lifetime extended, -/// // because the surrounding expression involves no function call. +/// // because the surrounding expression involves a function call. /// let p = ptr::from_mut(&mut foo()); /// unsafe { p.write(T::default()) }; // UB! Writing to a dangling pointer ⚠️ /// ``` @@ -1009,7 +1009,7 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_swap", since = "1.85.0")] #[rustc_diagnostic_item = "ptr_swap"] pub const unsafe fn swap(x: *mut T, y: *mut T) { // Give ourselves some scratch space to work with. @@ -2150,7 +2150,7 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// ``` /// /// [subtype]: https://doc.rust-lang.org/reference/subtyping.html -#[stable(feature = "ptr_fn_addr_eq", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "ptr_fn_addr_eq", since = "1.85.0")] #[inline(always)] #[must_use = "function pointer comparison produces a value"] pub fn fn_addr_eq(f: T, g: U) -> bool { diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index d75d570a969f..5d9d337f101a 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1594,7 +1594,7 @@ impl *mut T { /// /// [`ptr::swap`]: crate::ptr::swap() #[stable(feature = "pointer_methods", since = "1.26.0")] - #[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_swap", since = "1.85.0")] #[inline(always)] pub const unsafe fn swap(self, with: *mut T) where diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 1058fa42cc1e..2c9131254f7c 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -85,6 +85,20 @@ impl !Send for NonNull {} impl !Sync for NonNull {} impl NonNull { + /// Creates a pointer with the given address and no [provenance][crate::ptr#provenance]. + /// + /// For more details, see the equivalent method on a raw pointer, [`ptr::without_provenance_mut`]. + /// + /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. + #[unstable(feature = "nonnull_provenance", issue = "135243")] + pub const fn without_provenance(addr: NonZero) -> Self { + // SAFETY: we know `addr` is non-zero. + unsafe { + let ptr = crate::ptr::without_provenance_mut(addr.get()); + NonNull::new_unchecked(ptr) + } + } + /// Creates a new `NonNull` that is dangling, but well-aligned. /// /// This is useful for initializing types which lazily allocate, like @@ -116,6 +130,21 @@ impl NonNull { } } + /// Converts an address back to a mutable pointer, picking up some previously 'exposed' + /// [provenance][crate::ptr#provenance]. + /// + /// For more details, see the equivalent method on a raw pointer, [`ptr::with_exposed_provenance_mut`]. + /// + /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. + #[unstable(feature = "nonnull_provenance", issue = "135243")] + pub fn with_exposed_provenance(addr: NonZero) -> Self { + // SAFETY: we know `addr` is non-zero. + unsafe { + let ptr = crate::ptr::with_exposed_provenance_mut(addr.get()); + NonNull::new_unchecked(ptr) + } + } + /// Returns a shared references to the value. In contrast to [`as_ref`], this does not require /// that the value has to be initialized. /// @@ -224,7 +253,7 @@ impl NonNull { /// } /// ``` #[stable(feature = "nonnull", since = "1.25.0")] - #[rustc_const_stable(feature = "const_nonnull_new", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_nonnull_new", since = "1.85.0")] #[inline] pub const fn new(ptr: *mut T) -> Option { if !ptr.is_null() { @@ -282,7 +311,7 @@ impl NonNull { /// Gets the "address" portion of the pointer. /// - /// For more details see the equivalent method on a raw pointer, [`pointer::addr`]. + /// For more details, see the equivalent method on a raw pointer, [`pointer::addr`]. /// /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] @@ -294,10 +323,23 @@ impl NonNull { unsafe { NonZero::new_unchecked(self.as_ptr().addr()) } } + /// Exposes the ["provenance"][crate::ptr#provenance] part of the pointer for future use in + /// [`with_exposed_provenance`][NonNull::with_exposed_provenance] and returns the "address" portion. + /// + /// For more details, see the equivalent method on a raw pointer, [`pointer::expose_provenance`]. + /// + /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. + #[unstable(feature = "nonnull_provenance", issue = "135243")] + pub fn expose_provenance(self) -> NonZero { + // SAFETY: The pointer is guaranteed by the type to be non-null, + // meaning that the address will be non-zero. + unsafe { NonZero::new_unchecked(self.as_ptr().expose_provenance()) } + } + /// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of /// `self`. /// - /// For more details see the equivalent method on a raw pointer, [`pointer::with_addr`]. + /// For more details, see the equivalent method on a raw pointer, [`pointer::with_addr`]. /// /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] @@ -311,7 +353,7 @@ impl NonNull { /// Creates a new pointer by mapping `self`'s address to a new one, preserving the /// [provenance][crate::ptr#provenance] of `self`. /// - /// For more details see the equivalent method on a raw pointer, [`pointer::map_addr`]. + /// For more details, see the equivalent method on a raw pointer, [`pointer::map_addr`]. /// /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] @@ -1146,7 +1188,7 @@ impl NonNull { /// [`ptr::swap`]: crate::ptr::swap() #[inline(always)] #[stable(feature = "non_null_convenience", since = "1.80.0")] - #[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_swap", since = "1.85.0")] pub const unsafe fn swap(self, with: NonNull) where T: Sized, @@ -1556,7 +1598,6 @@ impl DispatchFromDyn> for NonNull where T: U unsafe impl PinCoerceUnsized for NonNull {} #[unstable(feature = "pointer_like_trait", issue = "none")] -#[cfg(not(bootstrap))] impl core::marker::PointerLike for NonNull {} #[stable(feature = "nonnull", since = "1.25.0")] diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index d2842f69008e..a687ed7129dc 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -49,7 +49,7 @@ impl<'a, T> IntoIterator for &'a mut [T] { /// // First, we need a slice to call the `iter` method on: /// let slice = &[1, 2, 3]; /// -/// // Then we call `iter` on the slice to get the `Iter` struct, +/// // Then we call `iter` on the slice to get the `Iter` iterator, /// // and iterate over it: /// for element in slice.iter() { /// println!("{element}"); @@ -107,24 +107,20 @@ impl<'a, T> Iter<'a, T> { /// Views the underlying data as a subslice of the original data. /// - /// This has the same lifetime as the original slice, and so the - /// iterator can continue to be used while this exists. - /// /// # Examples /// /// Basic usage: /// /// ``` /// // First, we need a slice to call the `iter` method on: - /// // struct (`&[usize]` here): /// let slice = &[1, 2, 3]; /// - /// // Then we call `iter` on the slice to get the `Iter` struct: + /// // Then we call `iter` on the slice to get the `Iter` iterator: /// let mut iter = slice.iter(); /// // Here `as_slice` still returns the whole slice, so this prints "[1, 2, 3]": /// println!("{:?}", iter.as_slice()); /// - /// // Now, we call the `next` method to remove the first element of the iterator: + /// // Now, we call the `next` method to remove the first element from the iterator: /// iter.next(); /// // Here the iterator does not contain the first element of the slice any more, /// // so `as_slice` only returns the last two elements of the slice, @@ -181,7 +177,7 @@ impl AsRef<[T]> for Iter<'_, T> { /// // First, we need a slice to call the `iter_mut` method on: /// let slice = &mut [1, 2, 3]; /// -/// // Then we call `iter_mut` on the slice to get the `IterMut` struct, +/// // Then we call `iter_mut` on the slice to get the `IterMut` iterator, /// // iterate over it and increment each element value: /// for element in slice.iter_mut() { /// *element += 1; @@ -286,25 +282,30 @@ impl<'a, T> IterMut<'a, T> { /// Views the underlying data as a subslice of the original data. /// - /// To avoid creating `&mut [T]` references that alias, the returned slice - /// borrows its lifetime from the iterator the method is applied on. - /// /// # Examples /// /// Basic usage: /// /// ``` - /// let mut slice: &mut [usize] = &mut [1, 2, 3]; + /// // First, we need a slice to call the `iter_mut` method on: + /// let slice = &mut [1, 2, 3]; /// - /// // First, we get the iterator: + /// // Then we call `iter_mut` on the slice to get the `IterMut` iterator: /// let mut iter = slice.iter_mut(); - /// // So if we check what the `as_slice` method returns here, we have "[1, 2, 3]": - /// assert_eq!(iter.as_slice(), &[1, 2, 3]); + /// // Here `as_slice` still returns the whole slice, so this prints "[1, 2, 3]": + /// println!("{:?}", iter.as_slice()); /// - /// // Next, we move to the second element of the slice: - /// iter.next(); - /// // Now `as_slice` returns "[2, 3]": - /// assert_eq!(iter.as_slice(), &[2, 3]); + /// // Now, we call the `next` method to remove the first element from the iterator + /// // and increment its value: + /// *iter.next().unwrap() += 1; + /// // Here the iterator does not contain the first element of the slice any more, + /// // so `as_slice` only returns the last two elements of the slice, + /// // and so this prints "[2, 3]": + /// println!("{:?}", iter.as_slice()); + /// + /// // The underlying slice still contains three elements, but its first element + /// // was increased by 1, so this prints "[2, 2, 3]": + /// println!("{:?}", slice); /// ``` #[must_use] #[stable(feature = "slice_iter_mut_as_slice", since = "1.53.0")] @@ -315,9 +316,6 @@ impl<'a, T> IterMut<'a, T> { /// Views the underlying data as a mutable subslice of the original data. /// - /// To avoid creating `&mut [T]` references that alias, the returned slice - /// borrows its lifetime from the iterator the method is applied on. - /// /// # Examples /// /// Basic usage: diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 073cca7daef0..5a22110880cc 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -913,7 +913,7 @@ impl [T] { /// assert!(v == ["a", "b", "e", "d", "c"]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_swap", since = "1.85.0")] #[inline] #[track_caller] pub const fn swap(&mut self, a: usize, b: usize) { @@ -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 { @@ -3701,6 +3703,7 @@ impl [T] { /// [`clone_from_slice`]: slice::clone_from_slice /// [`split_at_mut`]: slice::split_at_mut #[doc(alias = "memcpy")] + #[inline] #[stable(feature = "copy_from_slice", since = "1.9.0")] #[rustc_const_unstable(feature = "const_copy_from_slice", issue = "131415")] #[track_caller] diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index ba429005fab3..4c51ca0a5e43 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -60,7 +60,7 @@ impl RawWaker { RawWaker { data, vtable } } - #[stable(feature = "noop_waker", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "noop_waker", since = "1.85.0")] const NOOP: RawWaker = { const VTABLE: RawWakerVTable = RawWakerVTable::new( // Cloning just returns a new no-op raw waker @@ -564,8 +564,8 @@ impl Waker { /// ``` #[inline] #[must_use] - #[stable(feature = "noop_waker", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "noop_waker", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "noop_waker", since = "1.85.0")] + #[rustc_const_stable(feature = "noop_waker", since = "1.85.0")] pub const fn noop() -> &'static Waker { const WAKER: &Waker = &Waker { waker: RawWaker::NOOP }; WAKER diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 2d93148bac09..22bd46c567ea 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -21,6 +21,7 @@ use crate::fmt; use crate::iter::Sum; +use crate::num::niche_types::Nanoseconds; use crate::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; const NANOS_PER_SEC: u32 = 1_000_000_000; @@ -37,24 +38,6 @@ const HOURS_PER_DAY: u64 = 24; #[unstable(feature = "duration_units", issue = "120301")] const DAYS_PER_WEEK: u64 = 7; -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -#[rustc_layout_scalar_valid_range_end(999_999_999)] -struct Nanoseconds(u32); - -impl Nanoseconds { - // SAFETY: 0 is within the valid range - const ZERO: Self = unsafe { Nanoseconds(0) }; -} - -impl Default for Nanoseconds { - #[inline] - fn default() -> Self { - Self::ZERO - } -} - /// A `Duration` type to represent a span of time, typically used for system /// timeouts. /// @@ -211,14 +194,14 @@ impl Duration { pub const fn new(secs: u64, nanos: u32) -> Duration { if nanos < NANOS_PER_SEC { // SAFETY: nanos < NANOS_PER_SEC, therefore nanos is within the valid range - Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } + Duration { secs, nanos: unsafe { Nanoseconds::new_unchecked(nanos) } } } else { let secs = secs .checked_add((nanos / NANOS_PER_SEC) as u64) .expect("overflow in Duration::new"); let nanos = nanos % NANOS_PER_SEC; // SAFETY: nanos % NANOS_PER_SEC < NANOS_PER_SEC, therefore nanos is within the valid range - Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } + Duration { secs, nanos: unsafe { Nanoseconds::new_unchecked(nanos) } } } } @@ -263,7 +246,7 @@ impl Duration { let subsec_millis = (millis % MILLIS_PER_SEC) as u32; // SAFETY: (x % 1_000) * 1_000_000 < 1_000_000_000 // => x % 1_000 < 1_000 - let subsec_nanos = unsafe { Nanoseconds(subsec_millis * NANOS_PER_MILLI) }; + let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_millis * NANOS_PER_MILLI) }; Duration { secs, nanos: subsec_nanos } } @@ -289,7 +272,7 @@ impl Duration { let subsec_micros = (micros % MICROS_PER_SEC) as u32; // SAFETY: (x % 1_000_000) * 1_000 < 1_000_000_000 // => x % 1_000_000 < 1_000_000 - let subsec_nanos = unsafe { Nanoseconds(subsec_micros * NANOS_PER_MICRO) }; + let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_micros * NANOS_PER_MICRO) }; Duration { secs, nanos: subsec_nanos } } @@ -320,7 +303,7 @@ impl Duration { let secs = nanos / NANOS_PER_SEC; let subsec_nanos = (nanos % NANOS_PER_SEC) as u32; // SAFETY: x % 1_000_000_000 < 1_000_000_000 - let subsec_nanos = unsafe { Nanoseconds(subsec_nanos) }; + let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_nanos) }; Duration { secs, nanos: subsec_nanos } } @@ -458,7 +441,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_zero", since = "1.53.0")] #[inline] pub const fn is_zero(&self) -> bool { - self.secs == 0 && self.nanos.0 == 0 + self.secs == 0 && self.nanos.as_inner() == 0 } /// Returns the number of _whole_ seconds contained by this `Duration`. @@ -509,7 +492,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_millis(&self) -> u32 { - self.nanos.0 / NANOS_PER_MILLI + self.nanos.as_inner() / NANOS_PER_MILLI } /// Returns the fractional part of this `Duration`, in whole microseconds. @@ -532,7 +515,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_micros(&self) -> u32 { - self.nanos.0 / NANOS_PER_MICRO + self.nanos.as_inner() / NANOS_PER_MICRO } /// Returns the fractional part of this `Duration`, in nanoseconds. @@ -555,7 +538,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_nanos(&self) -> u32 { - self.nanos.0 + self.nanos.as_inner() } /// Returns the total number of whole milliseconds contained by this `Duration`. @@ -573,7 +556,8 @@ impl Duration { #[must_use] #[inline] pub const fn as_millis(&self) -> u128 { - self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MILLI) as u128 + self.secs as u128 * MILLIS_PER_SEC as u128 + + (self.nanos.as_inner() / NANOS_PER_MILLI) as u128 } /// Returns the total number of whole microseconds contained by this `Duration`. @@ -591,7 +575,8 @@ impl Duration { #[must_use] #[inline] pub const fn as_micros(&self) -> u128 { - self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MICRO) as u128 + self.secs as u128 * MICROS_PER_SEC as u128 + + (self.nanos.as_inner() / NANOS_PER_MICRO) as u128 } /// Returns the total number of nanoseconds contained by this `Duration`. @@ -609,7 +594,7 @@ impl Duration { #[must_use] #[inline] pub const fn as_nanos(&self) -> u128 { - self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.0 as u128 + self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.as_inner() as u128 } /// Computes the absolute difference between `self` and `other`. @@ -649,7 +634,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_add(self, rhs: Duration) -> Option { if let Some(mut secs) = self.secs.checked_add(rhs.secs) { - let mut nanos = self.nanos.0 + rhs.nanos.0; + let mut nanos = self.nanos.as_inner() + rhs.nanos.as_inner(); if nanos >= NANOS_PER_SEC { nanos -= NANOS_PER_SEC; if let Some(new_secs) = secs.checked_add(1) { @@ -707,11 +692,11 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_sub(self, rhs: Duration) -> Option { if let Some(mut secs) = self.secs.checked_sub(rhs.secs) { - let nanos = if self.nanos.0 >= rhs.nanos.0 { - self.nanos.0 - rhs.nanos.0 + let nanos = if self.nanos.as_inner() >= rhs.nanos.as_inner() { + self.nanos.as_inner() - rhs.nanos.as_inner() } else if let Some(sub_secs) = secs.checked_sub(1) { secs = sub_secs; - self.nanos.0 + NANOS_PER_SEC - rhs.nanos.0 + self.nanos.as_inner() + NANOS_PER_SEC - rhs.nanos.as_inner() } else { return None; }; @@ -763,7 +748,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_mul(self, rhs: u32) -> Option { // Multiply nanoseconds as u64, because it cannot overflow that way. - let total_nanos = self.nanos.0 as u64 * rhs as u64; + let total_nanos = self.nanos.as_inner() as u64 * rhs as u64; let extra_secs = total_nanos / (NANOS_PER_SEC as u64); let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32; // FIXME(const-hack): use `and_then` once that is possible. @@ -820,7 +805,8 @@ impl Duration { pub const fn checked_div(self, rhs: u32) -> Option { if rhs != 0 { let (secs, extra_secs) = (self.secs / (rhs as u64), self.secs % (rhs as u64)); - let (mut nanos, extra_nanos) = (self.nanos.0 / rhs, self.nanos.0 % rhs); + let (mut nanos, extra_nanos) = + (self.nanos.as_inner() / rhs, self.nanos.as_inner() % rhs); nanos += ((extra_secs * (NANOS_PER_SEC as u64) + extra_nanos as u64) / (rhs as u64)) as u32; debug_assert!(nanos < NANOS_PER_SEC); @@ -846,7 +832,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn as_secs_f64(&self) -> f64 { - (self.secs as f64) + (self.nanos.0 as f64) / (NANOS_PER_SEC as f64) + (self.secs as f64) + (self.nanos.as_inner() as f64) / (NANOS_PER_SEC as f64) } /// Returns the number of seconds contained by this `Duration` as `f32`. @@ -865,7 +851,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn as_secs_f32(&self) -> f32 { - (self.secs as f32) + (self.nanos.0 as f32) / (NANOS_PER_SEC as f32) + (self.secs as f32) + (self.nanos.as_inner() as f32) / (NANOS_PER_SEC as f32) } /// Returns the number of milliseconds contained by this `Duration` as `f64`. @@ -885,7 +871,7 @@ impl Duration { #[inline] pub const fn as_millis_f64(&self) -> f64 { (self.secs as f64) * (MILLIS_PER_SEC as f64) - + (self.nanos.0 as f64) / (NANOS_PER_MILLI as f64) + + (self.nanos.as_inner() as f64) / (NANOS_PER_MILLI as f64) } /// Returns the number of milliseconds contained by this `Duration` as `f32`. @@ -905,7 +891,7 @@ impl Duration { #[inline] pub const fn as_millis_f32(&self) -> f32 { (self.secs as f32) * (MILLIS_PER_SEC as f32) - + (self.nanos.0 as f32) / (NANOS_PER_MILLI as f32) + + (self.nanos.as_inner() as f32) / (NANOS_PER_MILLI as f32) } /// Creates a new `Duration` from the specified number of seconds represented @@ -1084,8 +1070,9 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn div_duration_f64(self, rhs: Duration) -> f64 { - let self_nanos = (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos.0 as f64); - let rhs_nanos = (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos.0 as f64); + let self_nanos = + (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos.as_inner() as f64); + let rhs_nanos = (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos.as_inner() as f64); self_nanos / rhs_nanos } @@ -1105,8 +1092,9 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn div_duration_f32(self, rhs: Duration) -> f32 { - let self_nanos = (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos.0 as f32); - let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos.0 as f32); + let self_nanos = + (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos.as_inner() as f32); + let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos.as_inner() as f32); self_nanos / rhs_nanos } } @@ -1201,13 +1189,13 @@ macro_rules! sum_durations { for entry in $iter { total_secs = total_secs.checked_add(entry.secs).expect("overflow in iter::sum over durations"); - total_nanos = match total_nanos.checked_add(entry.nanos.0 as u64) { + total_nanos = match total_nanos.checked_add(entry.nanos.as_inner() as u64) { Some(n) => n, None => { total_secs = total_secs .checked_add(total_nanos / NANOS_PER_SEC as u64) .expect("overflow in iter::sum over durations"); - (total_nanos % NANOS_PER_SEC as u64) + entry.nanos.0 as u64 + (total_nanos % NANOS_PER_SEC as u64) + entry.nanos.as_inner() as u64 } }; } @@ -1399,27 +1387,27 @@ impl fmt::Debug for Duration { let prefix = if f.sign_plus() { "+" } else { "" }; if self.secs > 0 { - fmt_decimal(f, self.secs, self.nanos.0, NANOS_PER_SEC / 10, prefix, "s") - } else if self.nanos.0 >= NANOS_PER_MILLI { + fmt_decimal(f, self.secs, self.nanos.as_inner(), NANOS_PER_SEC / 10, prefix, "s") + } else if self.nanos.as_inner() >= NANOS_PER_MILLI { fmt_decimal( f, - (self.nanos.0 / NANOS_PER_MILLI) as u64, - self.nanos.0 % NANOS_PER_MILLI, + (self.nanos.as_inner() / NANOS_PER_MILLI) as u64, + self.nanos.as_inner() % NANOS_PER_MILLI, NANOS_PER_MILLI / 10, prefix, "ms", ) - } else if self.nanos.0 >= NANOS_PER_MICRO { + } else if self.nanos.as_inner() >= NANOS_PER_MICRO { fmt_decimal( f, - (self.nanos.0 / NANOS_PER_MICRO) as u64, - self.nanos.0 % NANOS_PER_MICRO, + (self.nanos.as_inner() / NANOS_PER_MICRO) as u64, + self.nanos.as_inner() % NANOS_PER_MICRO, NANOS_PER_MICRO / 10, prefix, "µs", ) } else { - fmt_decimal(f, self.nanos.0 as u64, 0, 1, prefix, "ns") + fmt_decimal(f, self.nanos.as_inner() as u64, 0, 1, prefix, "ns") } } } diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index f3b4387f6a89..1b5c5fc82a69 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -200,60 +200,60 @@ fn uninit_array_assume_init() { } #[test] -fn uninit_write_slice() { +fn uninit_write_copy_of_slice() { let mut dst = [MaybeUninit::new(255); 64]; let src = [0; 64]; - assert_eq!(MaybeUninit::copy_from_slice(&mut dst, &src), &src); + assert_eq!(dst.write_copy_of_slice(&src), &src); } #[test] #[should_panic(expected = "source slice length (32) does not match destination slice length (64)")] -fn uninit_write_slice_panic_lt() { +fn uninit_write_copy_of_slice_panic_lt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 32]; - MaybeUninit::copy_from_slice(&mut dst, &src); + dst.write_copy_of_slice(&src); } #[test] #[should_panic(expected = "source slice length (128) does not match destination slice length (64)")] -fn uninit_write_slice_panic_gt() { +fn uninit_write_copy_of_slice_panic_gt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 128]; - MaybeUninit::copy_from_slice(&mut dst, &src); + dst.write_copy_of_slice(&src); } #[test] -fn uninit_clone_from_slice() { +fn uninit_write_clone_of_slice() { let mut dst = [MaybeUninit::new(255); 64]; let src = [0; 64]; - assert_eq!(MaybeUninit::clone_from_slice(&mut dst, &src), &src); + assert_eq!(dst.write_clone_of_slice(&src), &src); } #[test] #[should_panic(expected = "destination and source slices have different lengths")] -fn uninit_write_slice_cloned_panic_lt() { +fn uninit_write_clone_of_slice_panic_lt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 32]; - MaybeUninit::clone_from_slice(&mut dst, &src); + dst.write_clone_of_slice(&src); } #[test] #[should_panic(expected = "destination and source slices have different lengths")] -fn uninit_write_slice_cloned_panic_gt() { +fn uninit_write_clone_of_slice_panic_gt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 128]; - MaybeUninit::clone_from_slice(&mut dst, &src); + dst.write_clone_of_slice(&src); } #[test] #[cfg(panic = "unwind")] -fn uninit_write_slice_cloned_mid_panic() { +fn uninit_write_clone_of_slice_mid_panic() { use std::panic; enum IncrementOrPanic { @@ -289,7 +289,7 @@ fn uninit_write_slice_cloned_mid_panic() { ]; let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { - MaybeUninit::clone_from_slice(&mut dst, &src); + dst.write_clone_of_slice(&src); })); drop(src); @@ -317,11 +317,11 @@ impl Drop for Bomb { } #[test] -fn uninit_write_slice_cloned_no_drop() { +fn uninit_write_clone_of_slice_no_drop() { let mut dst = [MaybeUninit::uninit()]; let src = [Bomb]; - MaybeUninit::clone_from_slice(&mut dst, &src); + dst.write_clone_of_slice(&src); forget(src); } diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index e6825d8e39e2..7cefb615d037 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -304,7 +304,7 @@ fn test_const_nonnull_new() { #[test] #[cfg(unix)] // printf may not be available on other platforms #[allow(deprecated)] // For SipHasher -#[cfg_attr(not(bootstrap), allow(unpredictable_function_pointer_comparisons))] +#[allow(unpredictable_function_pointer_comparisons)] pub fn test_variadic_fnptr() { use core::ffi; use core::hash::{Hash, SipHasher}; 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/proc_macro/src/bridge/arena.rs b/library/proc_macro/src/bridge/arena.rs index 1d5986093c8a..29636e793f61 100644 --- a/library/proc_macro/src/bridge/arena.rs +++ b/library/proc_macro/src/bridge/arena.rs @@ -102,7 +102,7 @@ impl Arena { #[allow(clippy::mut_from_ref)] // arena allocator pub(crate) fn alloc_str<'a>(&'a self, string: &str) -> &'a mut str { let alloc = self.alloc_raw(string.len()); - let bytes = MaybeUninit::copy_from_slice(alloc, string.as_bytes()); + let bytes = alloc.write_copy_of_slice(string.as_bytes()); // SAFETY: we convert from `&str` to `&[u8]`, clone it into the arena, // and immediately convert the clone back to `&str`. diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 26a09f0daca0..b19c9cee75a0 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -419,7 +419,7 @@ pub mod token_stream { /// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term. /// To quote `$` itself, use `$$`. #[unstable(feature = "proc_macro_quote", issue = "54722")] -#[allow_internal_unstable(proc_macro_def_site, proc_macro_internals)] +#[allow_internal_unstable(proc_macro_def_site, proc_macro_internals, proc_macro_totokens)] #[rustc_builtin_macro] pub macro quote($($t:tt)*) { /* compiler built-in */ diff --git a/library/proc_macro/src/quote.rs b/library/proc_macro/src/quote.rs index 04fa696d5e6b..bcb15912bb65 100644 --- a/library/proc_macro/src/quote.rs +++ b/library/proc_macro/src/quote.rs @@ -4,12 +4,14 @@ //! This quasiquoter uses macros 2.0 hygiene to reliably access //! items from `proc_macro`, to build a `proc_macro::TokenStream`. -use crate::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; +use crate::{ + Delimiter, Group, Ident, Literal, Punct, Spacing, Span, ToTokens, TokenStream, TokenTree, +}; -macro_rules! quote_tt { - (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) }; - ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) }; - ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) }; +macro_rules! minimal_quote_tt { + (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, minimal_quote!($($t)*)) }; + ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, minimal_quote!($($t)*)) }; + ({$($t:tt)*}) => { Group::new(Delimiter::Brace, minimal_quote!($($t)*)) }; (,) => { Punct::new(',', Spacing::Alone) }; (.) => { Punct::new('.', Spacing::Alone) }; (;) => { Punct::new(';', Spacing::Alone) }; @@ -21,21 +23,20 @@ macro_rules! quote_tt { ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) }; } -macro_rules! quote_ts { +macro_rules! minimal_quote_ts { ((@ $($t:tt)*)) => { $($t)* }; (::) => { - [ - TokenTree::from(Punct::new(':', Spacing::Joint)), - TokenTree::from(Punct::new(':', Spacing::Alone)), - ].iter() - .cloned() - .map(|mut x| { - x.set_span(Span::def_site()); - x - }) - .collect::() + { + let mut c = ( + TokenTree::from(Punct::new(':', Spacing::Joint)), + TokenTree::from(Punct::new(':', Spacing::Alone)) + ); + c.0.set_span(Span::def_site()); + c.1.set_span(Span::def_site()); + [c.0, c.1].into_iter().collect::() + } }; - ($t:tt) => { TokenTree::from(quote_tt!($t)) }; + ($t:tt) => { TokenTree::from(minimal_quote_tt!($t)) }; } /// Simpler version of the real `quote!` macro, implemented solely @@ -46,12 +47,14 @@ macro_rules! quote_ts { /// /// Note: supported tokens are a subset of the real `quote!`, but /// unquoting is different: instead of `$x`, this uses `(@ expr)`. -macro_rules! quote { - () => { TokenStream::new() }; +macro_rules! minimal_quote { ($($t:tt)*) => { - [ - $(TokenStream::from(quote_ts!($t)),)* - ].iter().cloned().collect::() + { + #[allow(unused_mut)] // In case the expansion is empty + let mut ts = TokenStream::new(); + $(ToTokens::to_tokens(&minimal_quote_ts!($t), &mut ts);)* + ts + } }; } @@ -62,52 +65,66 @@ macro_rules! quote { #[unstable(feature = "proc_macro_quote", issue = "54722")] pub fn quote(stream: TokenStream) -> TokenStream { if stream.is_empty() { - return quote!(crate::TokenStream::new()); + return minimal_quote!(crate::TokenStream::new()); } - let proc_macro_crate = quote!(crate); + let proc_macro_crate = minimal_quote!(crate); let mut after_dollar = false; - let tokens = stream - .into_iter() - .filter_map(|tree| { - if after_dollar { - after_dollar = false; - match tree { - TokenTree::Ident(_) => { - return Some(quote!(Into::::into( - Clone::clone(&(@ tree))),)); - } - TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} - _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), - } - } else if let TokenTree::Punct(ref tt) = tree { - if tt.as_char() == '$' { - after_dollar = true; - return None; - } - } - Some(quote!(crate::TokenStream::from((@ match tree { - TokenTree::Punct(tt) => quote!(crate::TokenTree::Punct(crate::Punct::new( + let mut tokens = crate::TokenStream::new(); + for tree in stream { + if after_dollar { + after_dollar = false; + match tree { + TokenTree::Ident(_) => { + minimal_quote!(crate::ToTokens::to_tokens(&(@ tree), &mut ts);) + .to_tokens(&mut tokens); + continue; + } + TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} + _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), + } + } else if let TokenTree::Punct(ref tt) = tree { + if tt.as_char() == '$' { + after_dollar = true; + continue; + } + } + + match tree { + TokenTree::Punct(tt) => { + minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new( (@ TokenTree::from(Literal::character(tt.as_char()))), (@ match tt.spacing() { - Spacing::Alone => quote!(crate::Spacing::Alone), - Spacing::Joint => quote!(crate::Spacing::Joint), + Spacing::Alone => minimal_quote!(crate::Spacing::Alone), + Spacing::Joint => minimal_quote!(crate::Spacing::Joint), }), - ))), - TokenTree::Group(tt) => quote!(crate::TokenTree::Group(crate::Group::new( + )), &mut ts);) + } + TokenTree::Group(tt) => { + minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Group(crate::Group::new( (@ match tt.delimiter() { - Delimiter::Parenthesis => quote!(crate::Delimiter::Parenthesis), - Delimiter::Brace => quote!(crate::Delimiter::Brace), - Delimiter::Bracket => quote!(crate::Delimiter::Bracket), - Delimiter::None => quote!(crate::Delimiter::None), + Delimiter::Parenthesis => minimal_quote!(crate::Delimiter::Parenthesis), + Delimiter::Brace => minimal_quote!(crate::Delimiter::Brace), + Delimiter::Bracket => minimal_quote!(crate::Delimiter::Bracket), + Delimiter::None => minimal_quote!(crate::Delimiter::None), }), (@ quote(tt.stream())), - ))), - TokenTree::Ident(tt) => quote!(crate::TokenTree::Ident(crate::Ident::new( - (@ TokenTree::from(Literal::string(&tt.to_string()))), + )), &mut ts);) + } + TokenTree::Ident(tt) => { + let literal = tt.to_string(); + let (literal, ctor) = if let Some(stripped) = literal.strip_prefix("r#") { + (stripped, minimal_quote!(crate::Ident::new_raw)) + } else { + (literal.as_str(), minimal_quote!(crate::Ident::new)) + }; + minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Ident((@ ctor)( + (@ TokenTree::from(Literal::string(literal))), (@ quote_span(proc_macro_crate.clone(), tt.span())), - ))), - TokenTree::Literal(tt) => quote!(crate::TokenTree::Literal({ + )), &mut ts);) + } + TokenTree::Literal(tt) => { + minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Literal({ let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string()))) .parse::() .unwrap() @@ -120,16 +137,22 @@ pub fn quote(stream: TokenStream) -> TokenStream { } else { unreachable!() } - })) - })),)) - }) - .collect::(); - + }), &mut ts);) + } + } + .to_tokens(&mut tokens); + } if after_dollar { panic!("unexpected trailing `$` in `quote!`"); } - quote!([(@ tokens)].iter().cloned().collect::()) + minimal_quote! { + { + let mut ts = crate::TokenStream::new(); + (@ tokens) + ts + } + } } /// Quote a `Span` into a `TokenStream`. @@ -137,5 +160,5 @@ pub fn quote(stream: TokenStream) -> TokenStream { #[unstable(feature = "proc_macro_quote", issue = "54722")] pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream { let id = span.save_span(); - quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id))))) + minimal_quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id))))) } diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 56d734ba2fbf..d2342d8fd517 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -296,10 +296,7 @@ impl HashMap { /// ``` #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_stable( - feature = "const_collections_with_hasher", - since = "CURRENT_RUSTC_VERSION" - )] + #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] pub const fn with_hasher(hash_builder: S) -> HashMap { HashMap { base: base::HashMap::with_hasher(hash_builder) } } diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index bcf6d6b4ccf3..bbb6ca235213 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -388,10 +388,7 @@ impl HashSet { /// ``` #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_stable( - feature = "const_collections_with_hasher", - since = "CURRENT_RUSTC_VERSION" - )] + #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] pub const fn with_hasher(hasher: S) -> HashSet { HashSet { base: base::HashSet::with_hasher(hasher) } } diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 535236ff89a3..11a29cdae62e 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -622,7 +622,7 @@ impl Error for JoinPathsError { /// /// In UWP (Universal Windows Platform) targets this function is unimplemented and always returns `None`. /// -/// Before Rust CURRENT_RUSTC_VERSION, this function used to return the value of the 'HOME' environment variable +/// Before Rust 1.85.0, this function used to return the value of the 'HOME' environment variable /// on Windows, which in Cygwin or Mingw environments could return non-standard paths like `/home/you` /// instead of `C:\Users\you`. /// 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/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs index cbec44c86264..2572b71fd9ac 100644 --- a/library/std/src/ffi/os_str/tests.rs +++ b/library/std/src/ffi/os_str/tests.rs @@ -295,7 +295,7 @@ fn clone_to_uninit() { let mut storage = vec![MaybeUninit::::uninit(); size_of_val::(a)]; unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; - assert_eq!(a.as_encoded_bytes(), unsafe { MaybeUninit::slice_assume_init_ref(&storage) }); + assert_eq!(a.as_encoded_bytes(), unsafe { storage.assume_init_ref() }); let mut b: Box = OsStr::new("world.exe").into(); assert_eq!(size_of_val::(a), size_of_val::(&b)); diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 17721090db5d..5251cc302cb4 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -50,7 +50,7 @@ impl Buffer { pub fn buffer(&self) -> &[u8] { // SAFETY: self.pos and self.cap are valid, and self.cap => self.pos, and // that region is initialized because those are all invariants of this type. - unsafe { MaybeUninit::slice_assume_init_ref(self.buf.get_unchecked(self.pos..self.filled)) } + unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() } } #[inline] diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 476c403c21f3..38b723366175 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -339,7 +339,7 @@ pub enum ErrorKind { #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] NotSeekable, /// Filesystem quota or some other kind of quota was exceeded. - #[stable(feature = "io_error_quota_exceeded", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "io_error_quota_exceeded", since = "1.85.0")] QuotaExceeded, /// File larger than allowed or supported. /// @@ -364,7 +364,7 @@ pub enum ErrorKind { #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] Deadlock, /// Cross-device or cross-filesystem (hard) link or rename. - #[stable(feature = "io_error_crosses_devices", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "io_error_crosses_devices", since = "1.85.0")] CrossesDevices, /// Too many (hard) links to the same filesystem object. /// diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index f958a9386460..716da37168d0 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -103,7 +103,8 @@ //! the time. use core::marker::PhantomData; -use core::ptr::{self, NonNull}; +use core::num::NonZeroUsize; +use core::ptr::NonNull; use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; @@ -176,7 +177,7 @@ impl Repr { let utagged = ((code as usize) << 32) | TAG_OS; // Safety: `TAG_OS` is not zero, so the result of the `|` is not 0. let res = Self( - unsafe { NonNull::new_unchecked(ptr::without_provenance_mut(utagged)) }, + NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), PhantomData, ); // quickly smoke-check we encoded the right thing (This generally will @@ -193,7 +194,7 @@ impl Repr { let utagged = ((kind as usize) << 32) | TAG_SIMPLE; // Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. let res = Self( - unsafe { NonNull::new_unchecked(ptr::without_provenance_mut(utagged)) }, + NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), PhantomData, ); // quickly smoke-check we encoded the right thing (This generally will diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 2f8f5c5c5818..5c12236617c9 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -342,6 +342,7 @@ #![feature(lazy_get)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_write_slice)] +#![feature(nonnull_provenance)] #![feature(panic_can_unwind)] #![feature(panic_internals)] #![feature(pin_coerce_unsized_trait)] @@ -357,6 +358,7 @@ #![feature(str_internals)] #![feature(strict_provenance_atomic_ptr)] #![feature(sync_unsafe_cell)] +#![feature(temporary_niche_types)] #![feature(ub_checks)] #![feature(used_with_arg)] // tidy-alphabetical-end diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index abb13b75f508..1e814eca3c1a 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -11,6 +11,8 @@ use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fmt, fs, io}; +type ValidRawFd = core::num::niche_types::NotAllOnes; + /// A borrowed file descriptor. /// /// This has a lifetime parameter to tie it to the lifetime of something that owns the file @@ -32,15 +34,10 @@ use crate::{fmt, fs, io}; /// instead, but this is not supported on all platforms. #[derive(Copy, Clone)] #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct BorrowedFd<'fd> { - fd: RawFd, + fd: ValidRawFd, _phantom: PhantomData<&'fd OwnedFd>, } @@ -56,15 +53,10 @@ pub struct BorrowedFd<'fd> { /// /// You can use [`AsFd::as_fd`] to obtain a [`BorrowedFd`]. #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct OwnedFd { - fd: RawFd, + fd: ValidRawFd, } impl BorrowedFd<'_> { @@ -80,7 +72,8 @@ impl BorrowedFd<'_> { pub const unsafe fn borrow_raw(fd: RawFd) -> Self { assert!(fd != u32::MAX as RawFd); // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd, _phantom: PhantomData } } + let fd = unsafe { ValidRawFd::new_unchecked(fd) }; + Self { fd, _phantom: PhantomData } } } @@ -130,7 +123,7 @@ impl BorrowedFd<'_> { impl AsRawFd for BorrowedFd<'_> { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } @@ -138,7 +131,7 @@ impl AsRawFd for BorrowedFd<'_> { impl AsRawFd for OwnedFd { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } @@ -146,7 +139,7 @@ impl AsRawFd for OwnedFd { impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - ManuallyDrop::new(self).fd + ManuallyDrop::new(self).fd.as_inner() } } @@ -164,7 +157,8 @@ impl FromRawFd for OwnedFd { unsafe fn from_raw_fd(fd: RawFd) -> Self { assert_ne!(fd, u32::MAX as RawFd); // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd } } + let fd = unsafe { ValidRawFd::new_unchecked(fd) }; + Self { fd } } } @@ -187,12 +181,12 @@ impl Drop for OwnedFd { #[cfg(not(target_os = "hermit"))] { #[cfg(unix)] - crate::sys::fs::debug_assert_fd_is_open(self.fd); + crate::sys::fs::debug_assert_fd_is_open(self.fd.as_inner()); - let _ = libc::close(self.fd); + let _ = libc::close(self.fd.as_inner()); } #[cfg(target_os = "hermit")] - let _ = hermit_abi::close(self.fd); + let _ = hermit_abi::close(self.fd.as_inner()); } } } diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index 2d18f3396150..b8601b533fe0 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -54,6 +54,9 @@ use crate::{fmt, net, sys}; /// Raw file descriptors. pub type RawFd = i32; +// The max of this is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. +type ValidRawFd = core::num::niche_types::NotAllOnes; + /// A borrowed SOLID Sockets file descriptor. /// /// This has a lifetime parameter to tie it to the lifetime of something that @@ -69,12 +72,9 @@ pub type RawFd = i32; /// socket, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] pub struct BorrowedFd<'socket> { - fd: RawFd, + fd: ValidRawFd, _phantom: PhantomData<&'socket OwnedFd>, } @@ -87,12 +87,9 @@ pub struct BorrowedFd<'socket> { /// an argument, it is not captured or consumed, and it never has the value /// `SOLID_NET_INVALID_FD`. #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] pub struct OwnedFd { - fd: RawFd, + fd: ValidRawFd, } impl BorrowedFd<'_> { @@ -108,7 +105,8 @@ impl BorrowedFd<'_> { assert!(fd != -1 as RawFd); // SAFETY: we just asserted that the value is in the valid range and // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd, _phantom: PhantomData } } + let fd = unsafe { ValidRawFd::new_unchecked(fd) }; + Self { fd, _phantom: PhantomData } } } @@ -132,21 +130,21 @@ impl BorrowedFd<'_> { impl AsRawFd for BorrowedFd<'_> { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } impl AsRawFd for OwnedFd { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - ManuallyDrop::new(self).fd + ManuallyDrop::new(self).fd.as_inner() } } @@ -162,14 +160,15 @@ impl FromRawFd for OwnedFd { assert_ne!(fd, -1 as RawFd); // SAFETY: we just asserted that the value is in the valid range and // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd } } + let fd = unsafe { ValidRawFd::new_unchecked(fd) }; + Self { fd } } } impl Drop for OwnedFd { #[inline] fn drop(&mut self) { - unsafe { sys::net::netc::close(self.fd) }; + unsafe { sys::net::netc::close(self.fd.as_inner()) }; } } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index c6d7bad94409..6e13a8b502a7 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -9,6 +9,9 @@ use crate::mem::{self, ManuallyDrop}; use crate::sys::cvt; use crate::{fmt, io, sys}; +// The max here is -2, in two's complement. -1 is `INVALID_SOCKET`. +type ValidRawSocket = core::num::niche_types::NotAllOnes; + /// A borrowed socket. /// /// This has a lifetime parameter to tie it to the lifetime of something that @@ -24,17 +27,10 @@ use crate::{fmt, io, sys}; /// socket, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `INVALID_SOCKET`. -#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] -#[cfg_attr( - target_pointer_width = "64", - rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) -)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct BorrowedSocket<'socket> { - socket: RawSocket, + socket: ValidRawSocket, _phantom: PhantomData<&'socket OwnedSocket>, } @@ -47,17 +43,10 @@ pub struct BorrowedSocket<'socket> { /// argument or returned as an owned value, and it never has the value /// `INVALID_SOCKET`. #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `INVALID_SOCKET`. -#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] -#[cfg_attr( - target_pointer_width = "64", - rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) -)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct OwnedSocket { - socket: RawSocket, + socket: ValidRawSocket, } impl BorrowedSocket<'_> { @@ -73,7 +62,8 @@ impl BorrowedSocket<'_> { #[stable(feature = "io_safety", since = "1.63.0")] pub const unsafe fn borrow_raw(socket: RawSocket) -> Self { assert!(socket != sys::c::INVALID_SOCKET as RawSocket); - unsafe { Self { socket, _phantom: PhantomData } } + let socket = unsafe { ValidRawSocket::new_unchecked(socket) }; + Self { socket, _phantom: PhantomData } } } @@ -172,7 +162,7 @@ fn last_error() -> io::Error { impl AsRawSocket for BorrowedSocket<'_> { #[inline] fn as_raw_socket(&self) -> RawSocket { - self.socket + self.socket.as_inner() } } @@ -180,7 +170,7 @@ impl AsRawSocket for BorrowedSocket<'_> { impl AsRawSocket for OwnedSocket { #[inline] fn as_raw_socket(&self) -> RawSocket { - self.socket + self.socket.as_inner() } } @@ -188,7 +178,7 @@ impl AsRawSocket for OwnedSocket { impl IntoRawSocket for OwnedSocket { #[inline] fn into_raw_socket(self) -> RawSocket { - ManuallyDrop::new(self).socket + ManuallyDrop::new(self).socket.as_inner() } } @@ -196,10 +186,9 @@ impl IntoRawSocket for OwnedSocket { impl FromRawSocket for OwnedSocket { #[inline] unsafe fn from_raw_socket(socket: RawSocket) -> Self { - unsafe { - debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket); - Self { socket } - } + debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket); + let socket = unsafe { ValidRawSocket::new_unchecked(socket) }; + Self { socket } } } @@ -208,7 +197,7 @@ impl Drop for OwnedSocket { #[inline] fn drop(&mut self) { unsafe { - let _ = sys::c::closesocket(self.socket as sys::c::SOCKET); + let _ = sys::c::closesocket(self.socket.as_inner() as sys::c::SOCKET); } } } diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index ff3f7151bb83..3f96ac4672af 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -2069,9 +2069,7 @@ fn clone_to_uninit() { let mut storage = vec![MaybeUninit::::uninit(); size_of_val::(a)]; unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; - assert_eq!(a.as_os_str().as_encoded_bytes(), unsafe { - MaybeUninit::slice_assume_init_ref(&storage) - }); + assert_eq!(a.as_os_str().as_encoded_bytes(), unsafe { storage.assume_init_ref() }); let mut b: Box = Path::new("world.exe").into(); assert_eq!(size_of_val::(a), size_of_val::(&b)); 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/prelude/common.rs b/library/std/src/prelude/common.rs index 22a364074c52..0f2d8334fca7 100644 --- a/library/std/src/prelude/common.rs +++ b/library/std/src/prelude/common.rs @@ -12,8 +12,7 @@ pub use crate::marker::{Send, Sized, Sync, Unpin}; #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] #[doc(no_inline)] pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 64349987fcfb..4ec328208f01 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -169,12 +169,12 @@ pub mod rust_2021 { /// The 2024 version of the prelude of The Rust Standard Library. /// /// See the [module-level documentation](self) for more. -#[stable(feature = "prelude_2024", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "prelude_2024", since = "1.85.0")] pub mod rust_2024 { #[stable(feature = "rust1", since = "1.0.0")] pub use super::common::*; - #[stable(feature = "prelude_2024", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "prelude_2024", since = "1.85.0")] #[doc(no_inline)] pub use core::prelude::rust_2024::*; } 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/solid/fs.rs b/library/std/src/sys/pal/solid/fs.rs index 04dd10ad806d..fa2e470d6b60 100644 --- a/library/std/src/sys/pal/solid/fs.rs +++ b/library/std/src/sys/pal/solid/fs.rs @@ -12,15 +12,12 @@ use crate::sys::unsupported; pub use crate::sys_common::fs::exists; use crate::sys_common::ignore_notfound; +type CIntNotMinusOne = core::num::niche_types::NotAllOnes; + /// A file descriptor. #[derive(Clone, Copy)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] struct FileDesc { - fd: c_int, + fd: CIntNotMinusOne, } impl FileDesc { @@ -29,12 +26,13 @@ impl FileDesc { assert_ne!(fd, -1i32); // Safety: we just asserted that the value is in the valid range and // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { FileDesc { fd } } + let fd = unsafe { CIntNotMinusOne::new_unchecked(fd) }; + FileDesc { fd } } #[inline] fn raw(&self) -> c_int { - self.fd + self.fd.as_inner() } } diff --git a/library/std/src/sys/pal/uefi/fs.rs b/library/std/src/sys/pal/uefi/fs.rs new file mode 100644 index 000000000000..9585ec24f687 --- /dev/null +++ b/library/std/src/sys/pal/uefi/fs.rs @@ -0,0 +1,344 @@ +use crate::ffi::OsString; +use crate::fmt; +use crate::hash::{Hash, Hasher}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::path::{Path, PathBuf}; +use crate::sys::time::SystemTime; +use crate::sys::unsupported; + +pub struct File(!); + +pub struct FileAttr(!); + +pub struct ReadDir(!); + +pub struct DirEntry(!); + +#[derive(Clone, Debug)] +pub struct OpenOptions {} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes {} + +pub struct FilePermissions(!); + +pub struct FileType(!); + +#[derive(Debug)] +pub struct DirBuilder {} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.0 + } + + pub fn perm(&self) -> FilePermissions { + self.0 + } + + pub fn file_type(&self) -> FileType { + self.0 + } + + pub fn modified(&self) -> io::Result { + self.0 + } + + pub fn accessed(&self) -> io::Result { + self.0 + } + + pub fn created(&self) -> io::Result { + self.0 + } +} + +impl Clone for FileAttr { + fn clone(&self) -> FileAttr { + self.0 + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + self.0 + } + + pub fn set_readonly(&mut self, _readonly: bool) { + self.0 + } +} + +impl Clone for FilePermissions { + fn clone(&self) -> FilePermissions { + self.0 + } +} + +impl PartialEq for FilePermissions { + fn eq(&self, _other: &FilePermissions) -> bool { + self.0 + } +} + +impl Eq for FilePermissions {} + +impl fmt::Debug for FilePermissions { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl FileTimes { + pub fn set_accessed(&mut self, _t: SystemTime) {} + pub fn set_modified(&mut self, _t: SystemTime) {} +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.0 + } + + pub fn is_file(&self) -> bool { + self.0 + } + + pub fn is_symlink(&self) -> bool { + self.0 + } +} + +impl Clone for FileType { + fn clone(&self) -> FileType { + self.0 + } +} + +impl Copy for FileType {} + +impl PartialEq for FileType { + fn eq(&self, _other: &FileType) -> bool { + self.0 + } +} + +impl Eq for FileType {} + +impl Hash for FileType { + fn hash(&self, _h: &mut H) { + self.0 + } +} + +impl fmt::Debug for FileType { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + self.0 + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.0 + } + + pub fn file_name(&self) -> OsString { + self.0 + } + + pub fn metadata(&self) -> io::Result { + self.0 + } + + pub fn file_type(&self) -> io::Result { + self.0 + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions {} + } + + pub fn read(&mut self, _read: bool) {} + pub fn write(&mut self, _write: bool) {} + pub fn append(&mut self, _append: bool) {} + pub fn truncate(&mut self, _truncate: bool) {} + pub fn create(&mut self, _create: bool) {} + pub fn create_new(&mut self, _create_new: bool) {} +} + +impl File { + pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { + unsupported() + } + + pub fn file_attr(&self) -> io::Result { + self.0 + } + + pub fn fsync(&self) -> io::Result<()> { + self.0 + } + + pub fn datasync(&self) -> io::Result<()> { + self.0 + } + + pub fn lock(&self) -> io::Result<()> { + self.0 + } + + pub fn lock_shared(&self) -> io::Result<()> { + self.0 + } + + pub fn try_lock(&self) -> io::Result { + self.0 + } + + pub fn try_lock_shared(&self) -> io::Result { + self.0 + } + + pub fn unlock(&self) -> io::Result<()> { + self.0 + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + self.0 + } + + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn flush(&self) -> io::Result<()> { + self.0 + } + + pub fn seek(&self, _pos: SeekFrom) -> io::Result { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + self.0 + } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + self.0 + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder {} + } + + pub fn mkdir(&self, _p: &Path) -> io::Result<()> { + unsupported() + } +} + +impl fmt::Debug for File { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub fn readdir(_p: &Path) -> io::Result { + unsupported() +} + +pub fn unlink(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + unsupported() +} + +pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { + match perm.0 {} +} + +pub fn rmdir(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn remove_dir_all(_path: &Path) -> io::Result<()> { + unsupported() +} + +pub fn exists(_path: &Path) -> io::Result { + unsupported() +} + +pub fn readlink(_p: &Path) -> io::Result { + unsupported() +} + +pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn lstat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn canonicalize(_p: &Path) -> io::Result { + unsupported() +} + +pub fn copy(_from: &Path, _to: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index f29c91f3bfe6..111bed7a7eb6 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -15,7 +15,6 @@ pub mod args; pub mod env; -#[path = "../unsupported/fs.rs"] pub mod fs; pub mod helpers; #[path = "../unsupported/io.rs"] diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index e360ba0f6d7d..f657f82e6e36 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -130,25 +130,27 @@ impl Thread { } } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"))] pub fn set_name(name: &CStr) { - const TASK_COMM_LEN: usize = 16; - unsafe { - // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20. - let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + // Linux limits the allowed length of the name. + const TASK_COMM_LEN: usize = 16; + let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); + } else { + // FreeBSD and DragonFly BSD do not enforce length limits. + } + }; + // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux, + // FreeBSD 12.2 and 13.0, and DragonFly BSD 6.0. let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. debug_assert_eq!(res, 0); } } - #[cfg(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "openbsd", - target_os = "nuttx" - ))] + #[cfg(any(target_os = "openbsd", target_os = "nuttx"))] pub fn set_name(name: &CStr) { unsafe { libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index 343864d0b3fd..e224980e95f3 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -1,3 +1,5 @@ +use core::num::niche_types::Nanoseconds; + use crate::time::Duration; use crate::{fmt, io}; @@ -15,12 +17,6 @@ pub(in crate::sys) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec { tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64, }; -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -#[rustc_layout_scalar_valid_range_end(999_999_999)] -struct Nanoseconds(u32); - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SystemTime { pub(crate) t: Timespec, @@ -59,14 +55,14 @@ impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec.0) + .field("tv_nsec", &self.t.tv_nsec) .finish() } } impl Timespec { const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec { - Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } + Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } } } pub const fn zero() -> Timespec { @@ -147,12 +143,15 @@ impl Timespec { // // Ideally this code could be rearranged such that it more // directly expresses the lower-cost behavior we want from it. - let (secs, nsec) = if self.tv_nsec.0 >= other.tv_nsec.0 { - ((self.tv_sec - other.tv_sec) as u64, self.tv_nsec.0 - other.tv_nsec.0) + let (secs, nsec) = if self.tv_nsec.as_inner() >= other.tv_nsec.as_inner() { + ( + (self.tv_sec - other.tv_sec) as u64, + self.tv_nsec.as_inner() - other.tv_nsec.as_inner(), + ) } else { ( (self.tv_sec - other.tv_sec - 1) as u64, - self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0, + self.tv_nsec.as_inner() + (NSEC_PER_SEC as u32) - other.tv_nsec.as_inner(), ) }; @@ -170,7 +169,7 @@ impl Timespec { // Nano calculations can't overflow because nanos are <1B which fit // in a u32. - let mut nsec = other.subsec_nanos() + self.tv_nsec.0; + let mut nsec = other.subsec_nanos() + self.tv_nsec.as_inner(); if nsec >= NSEC_PER_SEC as u32 { nsec -= NSEC_PER_SEC as u32; secs = secs.checked_add(1)?; @@ -182,7 +181,7 @@ impl Timespec { let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?; // Similar to above, nanos can't overflow. - let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32; + let mut nsec = self.tv_nsec.as_inner() as i32 - other.subsec_nanos() as i32; if nsec < 0 { nsec += NSEC_PER_SEC as i32; secs = secs.checked_sub(1)?; @@ -194,7 +193,7 @@ impl Timespec { pub fn to_timespec(&self) -> Option { Some(libc::timespec { tv_sec: self.tv_sec.try_into().ok()?, - tv_nsec: self.tv_nsec.0.try_into().ok()?, + tv_nsec: self.tv_nsec.as_inner().try_into().ok()?, }) } @@ -203,7 +202,7 @@ impl Timespec { #[cfg(target_os = "nto")] pub(in crate::sys) fn to_timespec_capped(&self) -> Option { // Check if timeout in nanoseconds would fit into an u64 - if (self.tv_nsec.0 as u64) + if (self.tv_nsec.as_inner() as u64) .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?) .is_none() { @@ -219,7 +218,7 @@ impl Timespec { not(target_arch = "riscv32") ))] pub fn to_timespec64(&self) -> __timespec64 { - __timespec64::new(self.tv_sec, self.tv_nsec.0 as _) + __timespec64::new(self.tv_sec, self.tv_nsec.as_inner() as _) } } @@ -293,7 +292,7 @@ impl fmt::Debug for Instant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Instant") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec.0) + .field("tv_nsec", &self.t.tv_nsec) .finish() } } diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 88e6def7a757..4282dbb54934 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -272,7 +272,7 @@ where unreachable!(); } else { // Safety: First `k` values are initialized. - let slice: &[u16] = MaybeUninit::slice_assume_init_ref(&buf[..k]); + let slice: &[u16] = buf[..k].assume_init_ref(); return Ok(f2(slice)); } } 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/pal/windows/stdio.rs b/library/std/src/sys/pal/windows/stdio.rs index 1b735e7f0cb2..fd3f559ba190 100644 --- a/library/std/src/sys/pal/windows/stdio.rs +++ b/library/std/src/sys/pal/windows/stdio.rs @@ -207,7 +207,7 @@ fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result return Ok(bytes_copied + value), Err(e) => return Err(e), 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/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 85ee369ca2b6..b6ee00a253a9 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -502,7 +502,7 @@ impl Builder { let id = ThreadId::new(); let my_thread = match name { - Some(name) => Thread::new(id, name.into()), + Some(name) => Thread::new(id, name), None => Thread::new_unnamed(id), }; 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/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 7fd811ac5071..d8d862caf6a9 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -1,7 +1,5 @@ //! Implementation of compiling the compiler and standard library, in "check"-based modes. -use std::path::PathBuf; - use crate::core::build_steps::compile::{ add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make, }; @@ -10,7 +8,8 @@ use crate::core::builder::{ self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, crate_description, }; use crate::core::config::TargetSelection; -use crate::{Compiler, Mode, Subcommand}; +use crate::utils::build_stamp::{self, BuildStamp}; +use crate::{Mode, Subcommand}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Std { @@ -83,22 +82,16 @@ impl Step for Std { format_args!("library artifacts{}", crate_description(&self.crates)), target, ); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &libstd_stamp(builder, compiler, target), - vec![], - true, - false, - ); + + let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check"); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); // We skip populating the sysroot in non-zero stage because that'll lead // to rlib/rmeta conflicts if std gets built during this session. if compiler.stage == 0 { let libdir = builder.sysroot_target_libdir(compiler, target); let hostdir = builder.sysroot_target_libdir(compiler, compiler.host); - add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + add_to_sysroot(builder, &libdir, &hostdir, &stamp); } drop(_guard); @@ -139,16 +132,9 @@ impl Step for Std { cargo.arg("-p").arg(krate); } + let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check-test"); let _guard = builder.msg_check("library test/bench/example targets", target); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &libstd_test_stamp(builder, compiler, target), - vec![], - true, - false, - ); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } } @@ -249,19 +235,14 @@ impl Step for Rustc { format_args!("compiler artifacts{}", crate_description(&self.crates)), target, ); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &librustc_stamp(builder, compiler, target), - vec![], - true, - false, - ); + + let stamp = build_stamp::librustc_stamp(builder, compiler, target).with_prefix("check"); + + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); let libdir = builder.sysroot_target_libdir(compiler, target); let hostdir = builder.sysroot_target_libdir(compiler, compiler.host); - add_to_sysroot(builder, &libdir, &hostdir, &librustc_stamp(builder, compiler, target)); + add_to_sysroot(builder, &libdir, &hostdir, &stamp); } } @@ -315,15 +296,10 @@ impl Step for CodegenBackend { let _guard = builder.msg_check(backend, target); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &codegen_backend_stamp(builder, compiler, target, backend), - vec![], - true, - false, - ); + let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend) + .with_prefix("check"); + + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } } @@ -344,7 +320,7 @@ impl Step for RustAnalyzer { .config .tools .as_ref() - .map_or(true, |tools| tools.iter().any(|tool| tool == "rust-analyzer")), + .is_none_or(|tools| tools.iter().any(|tool| tool == "rust-analyzer")), ) } @@ -380,22 +356,13 @@ impl Step for RustAnalyzer { cargo.arg("--benches"); } - let _guard = builder.msg_check("rust-analyzer artifacts", target); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &stamp(builder, compiler, target), - vec![], - true, - false, - ); + // Cargo's output path in a given stage, compiled by a particular + // compiler for the specified target. + let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + .with_prefix("rust-analyzer-check"); - /// Cargo's output path in a given stage, compiled by a particular - /// compiler for the specified target. - fn stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::ToolRustc, target).join(".rust-analyzer-check.stamp") - } + let _guard = builder.msg_check("rust-analyzer artifacts", target); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } } @@ -469,9 +436,8 @@ fn run_tool_check_step( cargo.arg("--all-targets"); } - let stamp = builder - .cargo_out(compiler, Mode::ToolRustc, target) - .join(format!(".{}-check.stamp", step_type_name.to_lowercase())); + let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + .with_prefix(&format!("{}-check", step_type_name.to_lowercase())); let _guard = builder.msg_check(format!("{display_name} artifacts"), target); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); @@ -499,38 +465,3 @@ tool_check_step!(RunMakeSupport { path: "src/tools/run-make-support", default: f // Compiletest is implicitly "checked" when it gets built in order to run tests, // so this is mainly for people working on compiletest to run locally. tool_check_step!(Compiletest { path: "src/tools/compiletest", default: false }); - -/// Cargo's output path for the standard library in a given stage, compiled -/// by a particular compiler for the specified target. -fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check.stamp") -} - -/// Cargo's output path for the standard library in a given stage, compiled -/// by a particular compiler for the specified target. -fn libstd_test_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, -) -> PathBuf { - builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check-test.stamp") -} - -/// Cargo's output path for librustc in a given stage, compiled by a particular -/// compiler for the specified target. -fn librustc_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc-check.stamp") -} - -/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular -/// compiler for the specified target and backend. -fn codegen_backend_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, - backend: &str, -) -> PathBuf { - builder - .cargo_out(compiler, Mode::Codegen, target) - .join(format!(".librustc_codegen_{backend}-check.stamp")) -} diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index 61cc9eeed555..21e9fea93638 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -10,6 +10,7 @@ use std::io::{self, ErrorKind}; use std::path::Path; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step, crate_description}; +use crate::utils::build_stamp::BuildStamp; use crate::utils::helpers::t; use crate::{Build, Compiler, Kind, Mode, Subcommand}; @@ -146,7 +147,7 @@ fn clean_default(build: &Build) { rm_rf(&build.out.join("dist")); rm_rf(&build.out.join("bootstrap").join(".last-warned-change-id")); rm_rf(&build.out.join("bootstrap-shims-dump")); - rm_rf(&build.out.join("rustfmt.stamp")); + rm_rf(BuildStamp::new(&build.out).with_prefix("rustfmt").path()); let mut hosts: Vec<_> = build.hosts.iter().map(|t| build.out.join(t)).collect(); // After cross-compilation, artifacts of the host architecture (which may differ from build.host) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 518db156fea9..fe7e4a77f71a 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -1,12 +1,13 @@ //! Implementation of running clippy on the compiler, standard library and various tools. -use super::compile::{librustc_stamp, libstd_stamp, run_cargo, rustc_cargo, std_cargo}; +use super::compile::{run_cargo, rustc_cargo, std_cargo}; use super::tool::{SourceType, prepare_tool_cargo}; use super::{check, compile}; use crate::builder::{Builder, ShouldRun}; use crate::core::build_steps::compile::std_crates_for_run_make; use crate::core::builder; use crate::core::builder::{Alias, Kind, RunConfig, Step, crate_description}; +use crate::utils::build_stamp::{self, BuildStamp}; use crate::{Mode, Subcommand, TargetSelection}; /// Disable the most spammy clippy lints @@ -167,7 +168,7 @@ impl Step for Std { builder, cargo, lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), - &libstd_stamp(builder, compiler, target), + &build_stamp::libstd_stamp(builder, compiler, target), vec![], true, false, @@ -243,7 +244,7 @@ impl Step for Rustc { builder, cargo, lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), - &librustc_stamp(builder, compiler, target), + &build_stamp::librustc_stamp(builder, compiler, target), vec![], true, false, @@ -307,9 +308,9 @@ macro_rules! lint_any { &target, ); - let stamp = builder - .cargo_out(compiler, Mode::ToolRustc, target) - .join(format!(".{}-check.stamp", stringify!($name).to_lowercase())); + let stringified_name = stringify!($name).to_lowercase(); + let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + .with_prefix(&format!("{}-check", stringified_name)); run_cargo( builder, diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 148b96181d1d..4c4df922b379 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -24,6 +24,8 @@ use crate::core::builder::{ Builder, Cargo, Kind, PathSet, RunConfig, ShouldRun, Step, TaskPath, crate_description, }; use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection}; +use crate::utils::build_stamp; +use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::command; use crate::utils::helpers::{ exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date, @@ -254,7 +256,7 @@ impl Step for Std { builder, cargo, vec![], - &libstd_stamp(builder, compiler, target), + &build_stamp::libstd_stamp(builder, compiler, target), target_deps, self.is_for_mir_opt_tests, // is_check false, @@ -469,7 +471,7 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car // If `compiler-rt` is available ensure that the `c` feature of the // `compiler-builtins` crate is enabled and it's configured to learn where // `compiler-rt` is located. - let compiler_builtins_c_feature = if builder.config.optimized_compiler_builtins { + let compiler_builtins_c_feature = if builder.config.optimized_compiler_builtins(target) { // NOTE: this interacts strangely with `llvm-has-rust-patches`. In that case, we enforce `submodules = false`, so this is a no-op. // But, the user could still decide to manually use an in-tree submodule. // @@ -644,7 +646,12 @@ impl Step for StdLink { (libdir, hostdir) }; - add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + add_to_sysroot( + builder, + &libdir, + &hostdir, + &build_stamp::libstd_stamp(builder, compiler, target), + ); // Special case for stage0, to make `rustup toolchain link` and `x dist --stage 0` // work for stage0-sysroot. We only do this if the stage0 compiler comes from beta, @@ -973,7 +980,7 @@ impl Step for Rustc { compiler.host, target, ); - let stamp = librustc_stamp(builder, compiler, target); + let stamp = build_stamp::librustc_stamp(builder, compiler, target); run_cargo( builder, cargo, @@ -984,7 +991,7 @@ impl Step for Rustc { true, // Only ship rustc_driver.so and .rmeta files, not all intermediate .rlib files. ); - let target_root_dir = stamp.parent().unwrap(); + let target_root_dir = stamp.path().parent().unwrap(); // When building `librustc_driver.so` (like `libLLVM.so`) on linux, it can contain // unexpected debuginfo from dependencies, for example from the C++ standard library used in // our LLVM wrapper. Unless we're explicitly requesting `librustc_driver` to be built with @@ -1207,6 +1214,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 @@ -1320,7 +1336,7 @@ impl Step for RustcLink { builder, &builder.sysroot_target_libdir(previous_stage_compiler, target), &builder.sysroot_target_libdir(previous_stage_compiler, compiler.host), - &librustc_stamp(builder, compiler, target), + &build_stamp::librustc_stamp(builder, compiler, target), ); } } @@ -1438,7 +1454,7 @@ impl Step for CodegenBackend { .arg(builder.src.join(format!("compiler/rustc_codegen_{backend}/Cargo.toml"))); rustc_cargo_env(builder, &mut cargo, target, compiler.stage); - let tmp_stamp = out_dir.join(".tmp.stamp"); + let tmp_stamp = BuildStamp::new(&out_dir).with_prefix("tmp"); let _guard = builder.msg_build(compiler, format_args!("codegen backend {backend}"), target); let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false, false); @@ -1460,9 +1476,9 @@ impl Step for CodegenBackend { f.display() ); } - let stamp = codegen_backend_stamp(builder, compiler, target, &backend); + let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, &backend); let codegen_backend = codegen_backend.to_str().unwrap(); - t!(fs::write(stamp, codegen_backend)); + t!(stamp.add_stamp(codegen_backend).write()); } } @@ -1499,8 +1515,8 @@ fn copy_codegen_backends_to_sysroot( continue; // Already built as part of rustc } - let stamp = codegen_backend_stamp(builder, compiler, target, backend); - let dylib = t!(fs::read_to_string(&stamp)); + let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend); + let dylib = t!(fs::read_to_string(stamp.path())); let file = Path::new(&dylib); let filename = file.file_name().unwrap().to_str().unwrap(); // change `librustc_codegen_cranelift-xxxxxx.so` to @@ -1514,35 +1530,6 @@ fn copy_codegen_backends_to_sysroot( } } -/// Cargo's output path for the standard library in a given stage, compiled -/// by a particular compiler for the specified target. -pub fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::Std, target).join(".libstd.stamp") -} - -/// Cargo's output path for librustc in a given stage, compiled by a particular -/// compiler for the specified target. -pub fn librustc_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, -) -> PathBuf { - builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc.stamp") -} - -/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular -/// compiler for the specified target and backend. -fn codegen_backend_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, - backend: &str, -) -> PathBuf { - builder - .cargo_out(compiler, Mode::Codegen, target) - .join(format!(".librustc_codegen_{backend}.stamp")) -} - pub fn compiler_file( builder: &Builder<'_>, compiler: &Path, @@ -1664,10 +1651,10 @@ impl Step for Sysroot { ]; let ci_rustc_dir = builder.config.ci_rustc_dir(); builder.cp_link_filtered(&ci_rustc_dir, &sysroot, &|path| { - if path.extension().map_or(true, |ext| !filtered_extensions.contains(&ext)) { + if path.extension().is_none_or(|ext| !filtered_extensions.contains(&ext)) { return true; } - if !path.parent().map_or(true, |p| p.ends_with(&suffix)) { + if !path.parent().is_none_or(|p| p.ends_with(&suffix)) { return true; } if !filtered_files.iter().all(|f| f != path.file_name().unwrap()) { @@ -1899,7 +1886,7 @@ impl Step for Assemble { builder.info(&msg); // Link in all dylibs to the libdir - let stamp = librustc_stamp(builder, build_compiler, target_compiler.host); + let stamp = build_stamp::librustc_stamp(builder, build_compiler, target_compiler.host); let proc_macros = builder .read_stamp_file(&stamp) .into_iter() @@ -2005,7 +1992,7 @@ pub fn add_to_sysroot( builder: &Builder<'_>, sysroot_dst: &Path, sysroot_host_dst: &Path, - stamp: &Path, + stamp: &BuildStamp, ) { let self_contained_dst = &sysroot_dst.join("self-contained"); t!(fs::create_dir_all(sysroot_dst)); @@ -2025,13 +2012,13 @@ pub fn run_cargo( builder: &Builder<'_>, cargo: Cargo, tail_args: Vec, - stamp: &Path, + stamp: &BuildStamp, additional_target_deps: Vec<(PathBuf, DependencyType)>, is_check: bool, rlib_only_metadata: bool, ) -> Vec { // `target_root_dir` looks like $dir/$target/release - let target_root_dir = stamp.parent().unwrap(); + let target_root_dir = stamp.path().parent().unwrap(); // `target_deps_dir` looks like $dir/$target/release/deps let target_deps_dir = target_root_dir.join("deps"); // `host_root_dir` looks like $dir/release @@ -2184,7 +2171,7 @@ pub fn run_cargo( new_contents.extend(dep.to_str().unwrap().as_bytes()); new_contents.extend(b"\0"); } - t!(fs::write(stamp, &new_contents)); + t!(fs::write(stamp.path(), &new_contents)); deps.into_iter().map(|(d, _)| d).collect() } diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 8bfa9cc94b34..8ca087f9941f 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -23,6 +23,7 @@ use crate::core::build_steps::vendor::default_paths_to_vendor; use crate::core::build_steps::{compile, llvm}; use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; +use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::channel::{self, Info}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ @@ -47,7 +48,7 @@ fn should_build_extended_tool(builder: &Builder<'_>, tool: &str) -> bool { if !builder.config.extended { return false; } - builder.config.tools.as_ref().map_or(true, |tools| tools.contains(tool)) + builder.config.tools.as_ref().is_none_or(|tools| tools.contains(tool)) } #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] @@ -410,7 +411,7 @@ impl Step for Rustc { .config .tools .as_ref() - .map_or(true, |tools| tools.iter().any(|tool| tool == "rustdoc")) + .is_none_or(|tools| tools.iter().any(|tool| tool == "rustdoc")) { let rustdoc = builder.rustdoc(compiler); builder.install(&rustdoc, &image.join("bin"), 0o755); @@ -584,7 +585,7 @@ fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool { /// Check that all objects in rlibs for UEFI targets are COFF. This /// ensures that the C compiler isn't producing ELF objects, which would /// not link correctly with the COFF objects. -fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp: &Path) { +fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp: &BuildStamp) { if !target.ends_with("-uefi") { return; } @@ -615,7 +616,12 @@ fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp } /// Copy stamped files into an image's `target/lib` directory. -fn copy_target_libs(builder: &Builder<'_>, target: TargetSelection, image: &Path, stamp: &Path) { +fn copy_target_libs( + builder: &Builder<'_>, + target: TargetSelection, + image: &Path, + stamp: &BuildStamp, +) { let dst = image.join("lib/rustlib").join(target).join("lib"); let self_contained_dst = dst.join("self-contained"); t!(fs::create_dir_all(&dst)); @@ -668,7 +674,7 @@ impl Step for Std { tarball.include_target_in_component_name(true); let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); - let stamp = compile::libstd_stamp(builder, compiler_to_use, target); + let stamp = build_stamp::libstd_stamp(builder, compiler_to_use, target); verify_uefi_rlib_format(builder, target, &stamp); copy_target_libs(builder, target, tarball.image_dir(), &stamp); @@ -718,7 +724,7 @@ impl Step for RustcDev { let tarball = Tarball::new(builder, "rustc-dev", &target.triple); let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); - let stamp = compile::librustc_stamp(builder, compiler_to_use, target); + let stamp = build_stamp::librustc_stamp(builder, compiler_to_use, target); copy_target_libs(builder, target, tarball.image_dir(), &stamp); let src_files = &["Cargo.lock"]; @@ -1592,15 +1598,9 @@ impl Step for Extended { prepare("cargo"); prepare("rust-std"); prepare("rust-analysis"); - - for tool in &[ - "clippy", - "rustfmt", - "rust-analyzer", - "rust-docs", - "miri", - "rustc-codegen-cranelift", - ] { + prepare("clippy"); + prepare("rust-analyzer"); + for tool in &["rust-docs", "miri", "rustc-codegen-cranelift"] { if built_tools.contains(tool) { prepare(tool); } @@ -1640,8 +1640,6 @@ impl Step for Extended { "rust-analyzer-preview".to_string() } else if name == "clippy" { "clippy-preview".to_string() - } else if name == "rustfmt" { - "rustfmt-preview".to_string() } else if name == "miri" { "miri-preview".to_string() } else if name == "rustc-codegen-cranelift" { @@ -1661,7 +1659,7 @@ impl Step for Extended { prepare("cargo"); prepare("rust-analysis"); prepare("rust-std"); - for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] { + for tool in &["clippy", "rust-analyzer", "rust-docs", "miri"] { if built_tools.contains(tool) { prepare(tool); } @@ -1779,24 +1777,6 @@ impl Step for Extended { .arg(etc.join("msi/remove-duplicates.xsl")) .run(builder); } - if built_tools.contains("rustfmt") { - command(&heat) - .current_dir(&exe) - .arg("dir") - .arg("rustfmt") - .args(heat_flags) - .arg("-cg") - .arg("RustFmtGroup") - .arg("-dr") - .arg("RustFmt") - .arg("-var") - .arg("var.RustFmtDir") - .arg("-out") - .arg(exe.join("RustFmtGroup.wxs")) - .arg("-t") - .arg(etc.join("msi/remove-duplicates.xsl")) - .run(builder); - } if built_tools.contains("miri") { command(&heat) .current_dir(&exe) @@ -1868,9 +1848,6 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("-dClippyDir=clippy"); } - if built_tools.contains("rustfmt") { - cmd.arg("-dRustFmtDir=rustfmt"); - } if built_tools.contains("rust-docs") { cmd.arg("-dDocsDir=rust-docs"); } @@ -1897,9 +1874,6 @@ impl Step for Extended { if built_tools.contains("clippy") { candle("ClippyGroup.wxs".as_ref()); } - if built_tools.contains("rustfmt") { - candle("RustFmtGroup.wxs".as_ref()); - } if built_tools.contains("miri") { candle("MiriGroup.wxs".as_ref()); } @@ -1938,9 +1912,6 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("ClippyGroup.wixobj"); } - if built_tools.contains("rustfmt") { - cmd.arg("RustFmtGroup.wixobj"); - } if built_tools.contains("miri") { cmd.arg("MiriGroup.wixobj"); } diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index f15e0f38e692..40c908c3fa99 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -11,8 +11,9 @@ use build_helper::git::get_git_modified_files; use ignore::WalkBuilder; use crate::core::builder::Builder; +use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::command; -use crate::utils::helpers::{self, program_out_of_date, t}; +use crate::utils::helpers::{self, t}; #[must_use] enum RustfmtStatus { @@ -55,8 +56,8 @@ fn rustfmt( } } -fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, PathBuf)> { - let stamp_file = build.out.join("rustfmt.stamp"); +fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, BuildStamp)> { + let stamp_file = BuildStamp::new(&build.out).with_prefix("rustfmt"); let mut cmd = command(build.initial_rustfmt()?); cmd.arg("--version"); @@ -73,7 +74,7 @@ fn verify_rustfmt_version(build: &Builder<'_>) -> bool { let Some((version, stamp_file)) = get_rustfmt_version(build) else { return false; }; - !program_out_of_date(&stamp_file, &version) + stamp_file.add_stamp(version).is_up_to_date() } /// Updates the last rustfmt version used. @@ -81,7 +82,7 @@ fn update_rustfmt_version(build: &Builder<'_>) { let Some((version, stamp_file)) = get_rustfmt_version(build) else { return; }; - t!(std::fs::write(stamp_file, version)) + t!(std::fs::write(stamp_file.path(), version)) } /// Returns the Rust files modified between the `merge-base` of HEAD and @@ -114,6 +115,9 @@ fn print_paths(verb: &str, adjective: Option<&str>, paths: &[String]) { } else { println!("fmt: {verb} {len} {adjective}files"); } + if len > 1000 && !CiEnv::is_ci() { + println!("hint: if this number seems too high, try running `git fetch origin master"); + } } pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index b950bec11fd0..563b715fa640 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -12,14 +12,15 @@ use std::fs; use std::path::PathBuf; use std::sync::OnceLock; +use crate::Kind; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; +use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; -use crate::utils::helpers::{self, HashStamp, t}; -use crate::{Kind, generate_smart_stamp_hash}; +use crate::utils::helpers::{self, t}; pub struct Meta { - stamp: HashStamp, + stamp: BuildStamp, out_dir: PathBuf, install_dir: PathBuf, root: PathBuf, @@ -54,18 +55,17 @@ pub fn prebuilt_gcc_config(builder: &Builder<'_>, target: TargetSelection) -> Gc ) }); - let stamp = out_dir.join("gcc-finished-building"); - let stamp = HashStamp::new(stamp, Some(smart_stamp_hash)); + let stamp = BuildStamp::new(&out_dir).with_prefix("gcc").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info( "Could not determine the GCC submodule commit hash. \ Assuming that an GCC rebuild is not necessary.", ); builder.info(&format!( "To force GCC to rebuild, remove the file `{}`", - stamp.path.display() + stamp.path().display() )); } return GccBuildStatus::AlreadyBuilt; diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index b6862c2d5c4a..4f96d67a5bd8 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -307,7 +307,7 @@ impl Step for Src { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let config = &run.builder.config; - let cond = config.extended && config.tools.as_ref().map_or(true, |t| t.contains("src")); + let cond = config.extended && config.tools.as_ref().is_none_or(|t| t.contains("src")); run.path("src").default_condition(cond) } diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index be5b40570516..cf55fff4078a 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -8,23 +8,23 @@ //! LLVM and compiler-rt are essentially just wired up to everything else to //! ensure that they're always in place if needed. -use std::env; use std::env::consts::EXE_EXTENSION; use std::ffi::{OsStr, OsString}; -use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::sync::OnceLock; +use std::{env, fs}; use build_helper::ci::CiEnv; use build_helper::git::get_closest_merge_commit; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::{Config, TargetSelection}; +use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; use crate::utils::helpers::{ - self, HashStamp, exe, get_clang_cl_resource_dir, t, unhashed_basename, up_to_date, + self, exe, get_clang_cl_resource_dir, t, unhashed_basename, up_to_date, }; -use crate::{CLang, GitRepo, Kind, generate_smart_stamp_hash}; +use crate::{CLang, GitRepo, Kind}; #[derive(Clone)] pub struct LlvmResult { @@ -36,7 +36,7 @@ pub struct LlvmResult { } pub struct Meta { - stamp: HashStamp, + stamp: BuildStamp, res: LlvmResult, out_dir: PathBuf, root: String, @@ -135,18 +135,17 @@ pub fn prebuilt_llvm_config( ) }); - let stamp = out_dir.join("llvm-finished-building"); - let stamp = HashStamp::new(stamp, Some(smart_stamp_hash)); + let stamp = BuildStamp::new(&out_dir).with_prefix("llvm").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info( "Could not determine the LLVM submodule commit hash. \ Assuming that an LLVM rebuild is not necessary.", ); builder.info(&format!( "To force LLVM to rebuild, remove the file `{}`", - stamp.path.display() + stamp.path().display() )); } return LlvmBuildStatus::AlreadyBuilt(res); @@ -922,18 +921,17 @@ impl Step for Enzyme { }); let out_dir = builder.enzyme_out(target); - let stamp = out_dir.join("enzyme-finished-building"); - let stamp = HashStamp::new(stamp, Some(smart_stamp_hash)); + let stamp = BuildStamp::new(&out_dir).with_prefix("enzyme").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info( "Could not determine the Enzyme submodule commit hash. \ Assuming that an Enzyme rebuild is not necessary.", ); builder.info(&format!( "To force Enzyme to rebuild, remove the file `{}`", - stamp.path.display() + stamp.path().display() )); } return out_dir; @@ -1016,8 +1014,9 @@ impl Step for Lld { } let out_dir = builder.lld_out(target); - let done_stamp = out_dir.join("lld-finished-building"); - if done_stamp.exists() { + + let lld_stamp = BuildStamp::new(&out_dir).with_prefix("lld"); + if lld_stamp.path().exists() { return out_dir; } @@ -1092,7 +1091,7 @@ impl Step for Lld { cfg.build(); - t!(File::create(&done_stamp)); + t!(lld_stamp.write()); out_dir } } @@ -1122,25 +1121,32 @@ impl Step for Sanitizers { let out_dir = builder.native_dir(self.target).join("sanitizers"); let runtimes = supported_sanitizers(&out_dir, self.target, &builder.config.channel); - if runtimes.is_empty() { + + if builder.config.dry_run() || runtimes.is_empty() { return runtimes; } let LlvmResult { llvm_config, .. } = builder.ensure(Llvm { target: builder.config.build }); - if builder.config.dry_run() { - return runtimes; - } - let stamp = out_dir.join("sanitizers-finished-building"); - let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha()); + static STAMP_HASH_MEMO: OnceLock = OnceLock::new(); + let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| { + generate_smart_stamp_hash( + builder, + &builder.config.src.join("src/llvm-project/compiler-rt"), + builder.in_tree_llvm_info.sha().unwrap_or_default(), + ) + }); - if stamp.is_done() { - if stamp.hash.is_none() { + let stamp = BuildStamp::new(&out_dir).with_prefix("sanitizers").add_stamp(smart_stamp_hash); + + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info(&format!( "Rebuild sanitizers by removing the file `{}`", - stamp.path.display() + stamp.path().display() )); } + return runtimes; } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 64bcb0b85f4f..7d4404fa97b5 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -21,16 +21,18 @@ use crate::core::builder::{ }; use crate::core::config::TargetSelection; use crate::core::config::flags::{Subcommand, get_completion}; +use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ self, LldThreads, add_link_lib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var, 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"; +/// Runs `cargo test` on various internal tools used by bootstrap. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateBootstrap { path: PathBuf, @@ -43,13 +45,21 @@ impl Step for CrateBootstrap { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + // This step is responsible for several different tool paths. By default + // it will test all of them, but requesting specific tools on the + // command-line (e.g. `./x test suggest-tests`) will test only the + // specified tools. run.path("src/tools/jsondoclint") .path("src/tools/suggest-tests") .path("src/tools/replace-version-placeholder") + // We want `./x test tidy` to _run_ the tidy tool, not its tests. + // So we need a separate alias to test the tidy tool itself. .alias("tidyselftest") } fn make_run(run: RunConfig<'_>) { + // Create and ensure a separate instance of this step for each path + // that was selected on the command-line (or selected by default). for path in run.paths { let path = path.assert_single_path().path.clone(); run.builder.ensure(CrateBootstrap { host: run.target, path }); @@ -60,6 +70,8 @@ impl Step for CrateBootstrap { let bootstrap_host = builder.config.build; let compiler = builder.compiler(0, bootstrap_host); let mut path = self.path.to_str().unwrap(); + + // Map alias `tidyselftest` back to the actual crate path of tidy. if path == "tidyselftest" { path = "src/tools/tidy"; } @@ -212,6 +224,9 @@ impl Step for HtmlCheck { } } +/// Builds cargo and then runs the `src/tools/cargotest` tool, which checks out +/// some representative crate repositories and runs `cargo test` on them, in +/// order to test cargo. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Cargotest { stage: u32, @@ -257,6 +272,7 @@ impl Step for Cargotest { } } +/// Runs `cargo test` for cargo itself. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Cargo { stage: u32, @@ -385,6 +401,7 @@ impl Step for RustAnalyzer { } } +/// Runs `cargo test` for rustfmt. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Rustfmt { stage: u32, @@ -528,7 +545,7 @@ impl Step for Miri { // The mtime of `miri_sysroot` changes when the sysroot gets rebuilt (also see // ). // We can hence use that directly as a signal to clear the ui test dir. - builder.clear_if_dirty(&ui_test_dep_dir, &miri_sysroot); + build_stamp::clear_if_dirty(builder, &ui_test_dep_dir, &miri_sysroot); } // Run `cargo test`. @@ -589,6 +606,8 @@ impl Step for Miri { } } +/// Runs `cargo miri test` to demonstrate that `src/tools/miri/cargo-miri` +/// works and that libtest works under miri. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CargoMiri { target: TargetSelection, @@ -963,7 +982,7 @@ impl Step for RustdocGUI { let mut cmd = builder.tool_cmd(Tool::RustdocGUITest); let out_dir = builder.test_out(self.target).join("rustdoc-gui"); - builder.clear_if_dirty(&out_dir, &builder.rustdoc(self.compiler)); + build_stamp::clear_if_dirty(builder, &out_dir, &builder.rustdoc(self.compiler)); if let Some(src) = builder.config.src.to_str() { cmd.arg("--rust-src").arg(src); @@ -1020,6 +1039,10 @@ impl Step for RustdocGUI { } } +/// Runs `src/tools/tidy` and `cargo fmt --check` to detect various style +/// problems in the repository. +/// +/// (To run the tidy tool's internal tests, use the alias "tidyselftest" instead.) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Tidy; @@ -1185,53 +1208,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, @@ -1277,6 +1253,8 @@ impl Step for RunMakeSupport { } } +/// Runs `cargo test` on the `src/tools/run-make-support` crate. +/// That crate is used by run-make tests. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateRunMakeSupport { host: TargetSelection, @@ -1473,44 +1451,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 +1544,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", @@ -1663,7 +1638,10 @@ impl Step for Compiletest { return; } - if builder.top_stage == 0 && env::var("COMPILETEST_FORCE_STAGE0").is_err() { + if builder.top_stage == 0 + && env::var("COMPILETEST_FORCE_STAGE0").is_err() + && self.mode != "js-doc-test" + { eprintln!("\ ERROR: `--stage 0` runs compiletest on the beta compiler, not your local changes, and will almost always cause tests to fail HELP: to test the compiler, use `--stage 1` instead @@ -2284,10 +2262,8 @@ impl BookTest { &[], ); - let stamp = builder - .cargo_out(compiler, mode, target) - .join(PathBuf::from(dep).file_name().unwrap()) - .with_extension("stamp"); + let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, target)) + .with_prefix(PathBuf::from(dep).file_name().and_then(|v| v.to_str()).unwrap()); let output_paths = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); let directories = output_paths @@ -2516,6 +2492,10 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) -> } } +/// Runs `cargo test` for the compiler crates in `compiler/`. +/// +/// (This step does not test `rustc_codegen_cranelift` or `rustc_codegen_gcc`, +/// which have their own separate test steps.) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateLibrustc { compiler: Compiler, @@ -2544,6 +2524,7 @@ impl Step for CrateLibrustc { fn run(self, builder: &Builder<'_>) { builder.ensure(compile::Std::new(self.compiler, self.target)); + // To actually run the tests, delegate to a copy of the `Crate` step. builder.ensure(Crate { compiler: self.compiler, target: self.target, @@ -2669,6 +2650,13 @@ fn prepare_cargo_test( cargo } +/// Runs `cargo test` for standard library crates. +/// +/// (Also used internally to run `cargo test` for compiler crates.) +/// +/// FIXME(Zalathar): Try to split this into two separate steps: a user-visible +/// step for testing standard library crates, and an internal step used for both +/// library crates and compiler crates. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Crate { pub compiler: Compiler, @@ -3602,6 +3590,10 @@ impl Step for CodegenGCC { } } +/// Test step that does two things: +/// - Runs `cargo test` for the `src/etc/test-float-parse` tool. +/// - Invokes the `test-float-parse` tool to test the standard library's +/// float parsing routines. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TestFloatParse { path: PathBuf, @@ -3675,6 +3667,9 @@ impl Step for TestFloatParse { } } +/// Runs the tool `src/tools/collect-license-metadata` in `ONLY_CHECK=1` mode, +/// which verifies that `license-metadata.json` is up-to-date and therefore +/// running the tool normally would not update anything. #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] pub struct CollectLicenseMetadata; diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 432fbb8d6368..2692a129ef3b 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -7,6 +7,7 @@ use crate::core::build_steps::tool::SourceType; use crate::core::build_steps::{compile, test}; use crate::core::config::SplitDebuginfo; use crate::core::config::flags::Color; +use crate::utils::build_stamp; use crate::utils::helpers::{ self, LldThreads, add_link_lib_path, check_cfg_arg, linker_args, linker_flags, }; @@ -454,7 +455,7 @@ impl Builder<'_> { // Codegen backends are not yet tracked by -Zbinary-dep-depinfo, // so we need to explicitly clear out if they've been updated. for backend in self.codegen_backends(compiler) { - self.clear_if_dirty(&out_dir, &backend); + build_stamp::clear_if_dirty(self, &out_dir, &backend); } if cmd_kind == Kind::Doc { @@ -471,7 +472,7 @@ impl Builder<'_> { _ => panic!("doc mode {mode:?} not expected"), }; let rustdoc = self.rustdoc(compiler); - self.clear_if_dirty(&my_out, &rustdoc); + build_stamp::clear_if_dirty(self, &my_out, &rustdoc); } let profile_var = |name: &str| { @@ -624,8 +625,6 @@ impl Builder<'_> { // get warnings about it being unexpected. hostflags.arg("-Zunstable-options"); hostflags.arg("--check-cfg=cfg(bootstrap)"); - // #[cfg(bootstrap)] as we are transition `test` to userspace cfg - hostflags.arg("--check-cfg=cfg(test)"); // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See @@ -765,7 +764,7 @@ impl Builder<'_> { // Only clear out the directory if we're compiling std; otherwise, we // should let Cargo take care of things for us (via depdep info) if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build { - self.clear_if_dirty(&out_dir, &self.rustc(compiler)); + build_stamp::clear_if_dirty(self, &out_dir, &self.rustc(compiler)); } let rustdoc_path = match cmd_kind { @@ -1206,10 +1205,7 @@ impl Builder<'_> { // so that it'll be available when downstream consumers of std try to use it. rustflags.arg("-Zinline-mir-preserve-debug"); - // FIXME: always pass this after the next `#[cfg(bootstrap)]` update. - if compiler.stage != 0 { - rustflags.arg("-Zmir_strip_debuginfo=locals-in-tiny-functions"); - } + rustflags.arg("-Zmir_strip_debuginfo=locals-in-tiny-functions"); } Cargo { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 0660642c4d4f..17de17624273 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, @@ -1445,7 +1443,7 @@ impl<'a> Builder<'a> { let mut stack = self.stack.borrow_mut(); for stack_step in stack.iter() { // should skip - if stack_step.downcast_ref::().map_or(true, |stack_step| *stack_step != step) { + if stack_step.downcast_ref::().is_none_or(|stack_step| *stack_step != step) { continue; } let mut out = String::new(); 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/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index dd2f11ad4690..026380a46c68 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -634,6 +634,7 @@ pub struct Target { pub runner: Option, pub no_std: bool, pub codegen_backends: Option>, + pub optimized_compiler_builtins: Option, } impl Target { @@ -1125,7 +1126,7 @@ impl<'de> Deserialize<'de> for LldMode { match v { "external" => Ok(LldMode::External), "self-contained" => Ok(LldMode::SelfContained), - _ => Err(E::custom("unknown mode {v}")), + _ => Err(E::custom(format!("unknown mode {v}"))), } } } @@ -1219,6 +1220,7 @@ define_config! { no_std: Option = "no-std", codegen_backends: Option> = "codegen-backends", runner: Option = "runner", + optimized_compiler_builtins: Option = "optimized-compiler-builtins", } } @@ -2096,6 +2098,7 @@ impl Config { target.sanitizers = cfg.sanitizers; target.profiler = cfg.profiler; target.rpath = cfg.rpath; + target.optimized_compiler_builtins = cfg.optimized_compiler_builtins; if let Some(ref backends) = cfg.codegen_backends { let available_backends = ["llvm", "cranelift", "gcc"]; @@ -2609,6 +2612,13 @@ impl Config { self.target_config.get(&target).and_then(|t| t.rpath).unwrap_or(self.rust_rpath) } + pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> bool { + self.target_config + .get(&target) + .and_then(|t| t.optimized_compiler_builtins) + .unwrap_or(self.optimized_compiler_builtins) + } + pub fn llvm_enabled(&self, target: TargetSelection) -> bool { self.codegen_backends(target).contains(&"llvm".to_owned()) } @@ -3162,6 +3172,9 @@ fn check_incompatible_options_for_ci_rustc( let profiler = &ci_cfg.profiler; err!(current_cfg.profiler, profiler, "build"); + + let optimized_compiler_builtins = &ci_cfg.optimized_compiler_builtins; + err!(current_cfg.optimized_compiler_builtins, optimized_compiler_builtins, "build"); } } diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 24f932a17243..c5e578ff351e 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -120,6 +120,7 @@ fn override_toml() { "--set=change-id=1".to_owned(), "--set=rust.lto=fat".to_owned(), "--set=rust.deny-warnings=false".to_owned(), + "--set=build.optimized-compiler-builtins=true".to_owned(), "--set=build.gdb=\"bar\"".to_owned(), "--set=build.tools=[\"cargo\"]".to_owned(), "--set=llvm.build-config={\"foo\" = \"bar\"}".to_owned(), @@ -127,6 +128,7 @@ fn override_toml() { "--set=target.x86_64-unknown-linux-gnu.rpath=false".to_owned(), "--set=target.aarch64-unknown-linux-gnu.sanitizers=false".to_owned(), "--set=target.aarch64-apple-darwin.runner=apple".to_owned(), + "--set=target.aarch64-apple-darwin.optimized-compiler-builtins=false".to_owned(), ]), |&_| { toml::from_str( @@ -167,6 +169,7 @@ runner = "x86_64-runner" ); assert_eq!(config.gdb, Some("bar".into()), "setting string value with quotes"); assert!(!config.deny_warnings, "setting boolean value"); + assert!(config.optimized_compiler_builtins, "setting boolean value"); assert_eq!( config.tools, Some(["cargo".to_string()].into_iter().collect()), @@ -193,7 +196,11 @@ runner = "x86_64-runner" ..Default::default() }; let darwin = TargetSelection::from_user("aarch64-apple-darwin"); - let darwin_values = Target { runner: Some("apple".into()), ..Default::default() }; + let darwin_values = Target { + runner: Some("apple".into()), + optimized_compiler_builtins: Some(false), + ..Default::default() + }; assert_eq!( config.target_config, [(x86_64, x86_64_values), (aarch64, aarch64_values), (darwin, darwin_values)] diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index b5f7ed531312..c477bdb829a9 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -10,8 +10,9 @@ use build_helper::ci::CiEnv; use xz2::bufread::XzDecoder; use crate::core::config::BUILDER_CONFIG_FILENAME; +use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::{BootstrapCommand, command}; -use crate::utils::helpers::{check_run, exe, hex_encode, move_file, program_out_of_date}; +use crate::utils::helpers::{check_run, exe, hex_encode, move_file}; use crate::{Config, t}; static SHOULD_FIX_BINS_AND_DYLIBS: OnceLock = OnceLock::new(); @@ -46,7 +47,7 @@ impl Config { self.verbose > 0 } - pub(crate) fn create(&self, path: &Path, s: &str) { + pub(crate) fn create>(&self, path: P, s: &str) { if self.dry_run() { return; } @@ -426,9 +427,10 @@ impl Config { let version = &self.stage0_metadata.compiler.version; let host = self.build; - let clippy_stamp = self.initial_sysroot.join(".clippy-stamp"); + let clippy_stamp = + BuildStamp::new(&self.initial_sysroot).with_prefix("clippy").add_stamp(date); let cargo_clippy = self.initial_sysroot.join("bin").join(exe("cargo-clippy", host)); - if cargo_clippy.exists() && !program_out_of_date(&clippy_stamp, date) { + if cargo_clippy.exists() && clippy_stamp.is_up_to_date() { return cargo_clippy; } @@ -439,7 +441,7 @@ impl Config { self.fix_bin_or_dylib(&cargo_clippy.with_file_name(exe("clippy-driver", host))); } - self.create(&clippy_stamp, date); + t!(clippy_stamp.write()); cargo_clippy } @@ -460,8 +462,8 @@ impl Config { let host = self.build; let bin_root = self.out.join(host).join("rustfmt"); let rustfmt_path = bin_root.join("bin").join(exe("rustfmt", host)); - let rustfmt_stamp = bin_root.join(".rustfmt-stamp"); - if rustfmt_path.exists() && !program_out_of_date(&rustfmt_stamp, &channel) { + let rustfmt_stamp = BuildStamp::new(&bin_root).with_prefix("rustfmt").add_stamp(channel); + if rustfmt_path.exists() && rustfmt_stamp.is_up_to_date() { return Some(rustfmt_path); } @@ -492,7 +494,7 @@ impl Config { } } - self.create(&rustfmt_stamp, &channel); + t!(rustfmt_stamp.write()); Some(rustfmt_path) } @@ -567,10 +569,10 @@ impl Config { ) { let host = self.build.triple; let bin_root = self.out.join(host).join(sysroot); - let rustc_stamp = bin_root.join(".rustc-stamp"); + let rustc_stamp = BuildStamp::new(&bin_root).with_prefix("rustc").add_stamp(stamp_key); if !bin_root.join("bin").join(exe("rustc", self.build)).exists() - || program_out_of_date(&rustc_stamp, stamp_key) + || !rustc_stamp.is_up_to_date() { if bin_root.exists() { t!(fs::remove_dir_all(&bin_root)); @@ -601,7 +603,7 @@ impl Config { } } - t!(fs::write(rustc_stamp, stamp_key)); + t!(rustc_stamp.write()); } } @@ -728,10 +730,10 @@ download-rustc = false } let llvm_root = self.ci_llvm_root(); - let llvm_stamp = llvm_root.join(".llvm-stamp"); let llvm_sha = detect_llvm_sha(self, self.rust_info.is_managed_git_subrepository()); - let key = format!("{}{}", llvm_sha, self.llvm_assertions); - if program_out_of_date(&llvm_stamp, &key) && !self.dry_run() { + let stamp_key = format!("{}{}", llvm_sha, self.llvm_assertions); + let llvm_stamp = BuildStamp::new(&llvm_root).with_prefix("llvm").add_stamp(stamp_key); + if !llvm_stamp.is_up_to_date() && !self.dry_run() { self.download_ci_llvm(&llvm_sha); if self.should_fix_bins_and_dylibs() { @@ -764,7 +766,7 @@ download-rustc = false } } - t!(fs::write(llvm_stamp, key)); + t!(llvm_stamp.write()); } if let Some(config_path) = &self.config { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 8405c22aff08..ebe2c332258d 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -19,27 +19,23 @@ use std::cell::{Cell, RefCell}; use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::Display; -use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::OnceLock; use std::time::SystemTime; -use std::{env, io, str}; +use std::{env, fs, io, str}; use build_helper::ci::gha; use build_helper::exit; -use sha2::digest::Digest; use termcolor::{ColorChoice, StandardStream, WriteColor}; +use utils::build_stamp::BuildStamp; use utils::channel::GitInfo; -use utils::helpers::hex_encode; use crate::core::builder; -use crate::core::builder::{Builder, Kind}; +use crate::core::builder::Kind; use crate::core::config::{DryRun, LldMode, LlvmLibunwind, Target, TargetSelection, flags}; use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, CommandOutput, OutputMode, command}; -use crate::utils::helpers::{ - self, dir_is_empty, exe, libdir, mtime, output, set_file_times, symlink_dir, -}; +use crate::utils::helpers::{self, dir_is_empty, exe, libdir, output, set_file_times, symlink_dir}; mod core; mod utils; @@ -77,8 +73,6 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"]; #[allow(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above. const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ (None, "bootstrap", None), - // #[cfg(bootstrap)] to be removed when Cargo is updated - (None, "test", None), (Some(Mode::Rustc), "llvm_enzyme", None), (Some(Mode::Codegen), "llvm_enzyme", None), (Some(Mode::ToolRustc), "llvm_enzyme", None), @@ -600,24 +594,6 @@ impl Build { self.metrics.persist(self); } - /// Clear out `dir` if `input` is newer. - /// - /// After this executes, it will also ensure that `dir` exists. - fn clear_if_dirty(&self, dir: &Path, input: &Path) -> bool { - let stamp = dir.join(".stamp"); - let mut cleared = false; - if mtime(&stamp) < mtime(input) { - self.verbose(|| println!("Dirty - {}", dir.display())); - let _ = fs::remove_dir_all(dir); - cleared = true; - } else if stamp.exists() { - return cleared; - } - t!(fs::create_dir_all(dir)); - t!(File::create(stamp)); - cleared - } - fn rust_info(&self) -> &GitInfo { &self.config.rust_info } @@ -1624,21 +1600,21 @@ Executed at: {executed_at}"#, ret } - fn read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, DependencyType)> { + fn read_stamp_file(&self, stamp: &BuildStamp) -> Vec<(PathBuf, DependencyType)> { if self.config.dry_run() { return Vec::new(); } - if !stamp.exists() { + if !stamp.path().exists() { eprintln!( "ERROR: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?", - stamp.display() + stamp.path().display() ); crate::exit!(1); } let mut paths = Vec::new(); - let contents = t!(fs::read(stamp), &stamp); + let contents = t!(fs::read(stamp.path()), stamp.path()); // This is the method we use for extracting paths from the stamp file passed to us. See // run_cargo for more information (in compile.rs). for part in contents.split(|b| *b == 0) { @@ -1924,52 +1900,6 @@ fn envify(s: &str) -> String { .collect() } -/// Computes a hash representing the state of a repository/submodule and additional input. -/// -/// It uses `git diff` for the actual changes, and `git status` for including the untracked -/// files in the specified directory. The additional input is also incorporated into the -/// computation of the hash. -/// -/// # Parameters -/// -/// - `dir`: A reference to the directory path of the target repository/submodule. -/// - `additional_input`: An additional input to be included in the hash. -/// -/// # Panics -/// -/// In case of errors during `git` command execution (e.g., in tarball sources), default values -/// are used to prevent panics. -pub fn generate_smart_stamp_hash( - builder: &Builder<'_>, - dir: &Path, - additional_input: &str, -) -> String { - let diff = helpers::git(Some(dir)) - .allow_failure() - .arg("diff") - .run_capture_stdout(builder) - .stdout_if_ok() - .unwrap_or_default(); - - let status = helpers::git(Some(dir)) - .allow_failure() - .arg("status") - .arg("--porcelain") - .arg("-z") - .arg("--untracked-files=normal") - .run_capture_stdout(builder) - .stdout_if_ok() - .unwrap_or_default(); - - let mut hasher = sha2::Sha256::new(); - - hasher.update(diff); - hasher.update(status); - hasher.update(additional_input); - - hex_encode(hasher.finalize().as_slice()) -} - /// Ensures that the behavior dump directory is properly initialized. pub fn prepare_behaviour_dump_dir(build: &Build) { static INITIALIZED: OnceLock = OnceLock::new(); diff --git a/src/bootstrap/src/utils/build_stamp.rs b/src/bootstrap/src/utils/build_stamp.rs new file mode 100644 index 000000000000..f43d860893f6 --- /dev/null +++ b/src/bootstrap/src/utils/build_stamp.rs @@ -0,0 +1,204 @@ +//! Module for managing build stamp files. +//! +//! Contains the core implementation of how bootstrap utilizes stamp files on build processes. + +use std::path::{Path, PathBuf}; +use std::{fs, io}; + +use sha2::digest::Digest; + +use crate::core::builder::Builder; +use crate::core::config::TargetSelection; +use crate::utils::helpers::{hex_encode, mtime}; +use crate::{Compiler, Mode, helpers, t}; + +#[cfg(test)] +mod tests; + +/// Manages a stamp file to track build state. The file is created in the given +/// directory and can have custom content and name. +#[derive(Clone)] +pub struct BuildStamp { + path: PathBuf, + stamp: String, +} + +impl BuildStamp { + /// Creates a new `BuildStamp` for a given directory. + /// + /// By default, stamp will be an empty file named `.stamp` within the specified directory. + pub fn new(dir: &Path) -> Self { + // Avoid using `is_dir()` as the directory may not exist yet. + // It is more appropriate to assert that the path is not a file. + assert!(!dir.is_file(), "can't be a file path"); + Self { path: dir.join(".stamp"), stamp: String::new() } + } + + /// Returns path of the stamp file. + pub fn path(&self) -> &Path { + &self.path + } + + /// Returns the value of the stamp. + /// + /// Note that this is empty by default and is populated using `BuildStamp::add_stamp`. + /// It is not read from an actual file, but rather it holds the value that will be used + /// when `BuildStamp::write` is called. + pub fn stamp(&self) -> &str { + &self.stamp + } + + /// Adds specified stamp content to the current value. + /// + /// This method can be used incrementally e.g., `add_stamp("x").add_stamp("y").add_stamp("z")`. + pub fn add_stamp(mut self, stamp: S) -> Self { + self.stamp.push_str(&stamp.to_string()); + self + } + + /// Adds a prefix to stamp's name. + /// + /// Prefix cannot start or end with a dot (`.`). + pub fn with_prefix(mut self, prefix: &str) -> Self { + assert!( + !prefix.starts_with('.') && !prefix.ends_with('.'), + "prefix can not start or end with '.'" + ); + + let stamp_filename = self.path.file_name().unwrap().to_str().unwrap(); + let stamp_filename = stamp_filename.strip_prefix('.').unwrap_or(stamp_filename); + self.path.set_file_name(format!(".{prefix}-{stamp_filename}")); + + self + } + + /// Removes the stamp file if it exists. + pub fn remove(&self) -> io::Result<()> { + match fs::remove_file(&self.path) { + Ok(()) => Ok(()), + Err(e) => { + if e.kind() == io::ErrorKind::NotFound { + Ok(()) + } else { + Err(e) + } + } + } + } + + /// Creates the stamp file. + pub fn write(&self) -> io::Result<()> { + fs::write(&self.path, &self.stamp) + } + + /// Checks if the stamp file is up-to-date. + /// + /// It is considered up-to-date if file content matches with the stamp string. + pub fn is_up_to_date(&self) -> bool { + match fs::read(&self.path) { + Ok(h) => self.stamp.as_bytes() == h.as_slice(), + Err(e) if e.kind() == io::ErrorKind::NotFound => false, + Err(e) => { + panic!("failed to read stamp file `{}`: {}", self.path.display(), e); + } + } + } +} + +/// Clear out `dir` if `input` is newer. +/// +/// After this executes, it will also ensure that `dir` exists. +pub fn clear_if_dirty(builder: &Builder<'_>, dir: &Path, input: &Path) -> bool { + let stamp = BuildStamp::new(dir); + let mut cleared = false; + if mtime(stamp.path()) < mtime(input) { + builder.verbose(|| println!("Dirty - {}", dir.display())); + let _ = fs::remove_dir_all(dir); + cleared = true; + } else if stamp.path().exists() { + return cleared; + } + t!(fs::create_dir_all(dir)); + t!(fs::File::create(stamp.path())); + cleared +} + +/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular +/// compiler for the specified target and backend. +pub fn codegen_backend_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, + backend: &str, +) -> BuildStamp { + BuildStamp::new(&builder.cargo_out(compiler, Mode::Codegen, target)) + .with_prefix(&format!("librustc_codegen_{backend}")) +} + +/// Cargo's output path for the standard library in a given stage, compiled +/// by a particular compiler for the specified target. +pub fn libstd_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, +) -> BuildStamp { + BuildStamp::new(&builder.cargo_out(compiler, Mode::Std, target)).with_prefix("libstd") +} + +/// Cargo's output path for librustc in a given stage, compiled by a particular +/// compiler for the specified target. +pub fn librustc_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, +) -> BuildStamp { + BuildStamp::new(&builder.cargo_out(compiler, Mode::Rustc, target)).with_prefix("librustc") +} + +/// Computes a hash representing the state of a repository/submodule and additional input. +/// +/// It uses `git diff` for the actual changes, and `git status` for including the untracked +/// files in the specified directory. The additional input is also incorporated into the +/// computation of the hash. +/// +/// # Parameters +/// +/// - `dir`: A reference to the directory path of the target repository/submodule. +/// - `additional_input`: An additional input to be included in the hash. +/// +/// # Panics +/// +/// In case of errors during `git` command execution (e.g., in tarball sources), default values +/// are used to prevent panics. +pub fn generate_smart_stamp_hash( + builder: &Builder<'_>, + dir: &Path, + additional_input: &str, +) -> String { + let diff = helpers::git(Some(dir)) + .allow_failure() + .arg("diff") + .arg(".") + .run_capture_stdout(builder) + .stdout_if_ok() + .unwrap_or_default(); + + let status = helpers::git(Some(dir)) + .allow_failure() + .arg("status") + .arg(".") + .arg("--porcelain") + .arg("-z") + .arg("--untracked-files=normal") + .run_capture_stdout(builder) + .stdout_if_ok() + .unwrap_or_default(); + + let mut hasher = sha2::Sha256::new(); + + hasher.update(diff); + hasher.update(status); + hasher.update(additional_input); + + hex_encode(hasher.finalize().as_slice()) +} diff --git a/src/bootstrap/src/utils/build_stamp/tests.rs b/src/bootstrap/src/utils/build_stamp/tests.rs new file mode 100644 index 000000000000..2d7d1f712465 --- /dev/null +++ b/src/bootstrap/src/utils/build_stamp/tests.rs @@ -0,0 +1,60 @@ +use std::path::PathBuf; + +use crate::{BuildStamp, Config, Flags}; + +fn temp_dir() -> PathBuf { + let config = + Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); + config.tempdir() +} + +#[test] +#[should_panic(expected = "prefix can not start or end with '.'")] +fn test_with_invalid_prefix() { + let dir = temp_dir(); + BuildStamp::new(&dir).with_prefix(".invalid"); +} + +#[test] +#[should_panic(expected = "prefix can not start or end with '.'")] +fn test_with_invalid_prefix2() { + let dir = temp_dir(); + BuildStamp::new(&dir).with_prefix("invalid."); +} + +#[test] +fn test_is_up_to_date() { + let dir = temp_dir(); + + let mut build_stamp = BuildStamp::new(&dir).add_stamp("v1.0.0"); + build_stamp.write().unwrap(); + + assert!( + build_stamp.is_up_to_date(), + "Expected stamp file to be up-to-date, but contents do not match the expected value." + ); + + build_stamp.stamp = "dummy value".to_owned(); + assert!( + !build_stamp.is_up_to_date(), + "Stamp should no longer be up-to-date as we changed its content right above." + ); + + build_stamp.remove().unwrap(); +} + +#[test] +fn test_with_prefix() { + let dir = temp_dir(); + + let stamp = BuildStamp::new(&dir).add_stamp("v1.0.0"); + assert_eq!(stamp.path.file_name().unwrap(), ".stamp"); + + let stamp = stamp.with_prefix("test"); + let expected_filename = ".test-stamp"; + assert_eq!(stamp.path.file_name().unwrap(), expected_filename); + + let stamp = stamp.with_prefix("extra-prefix"); + let expected_filename = ".extra-prefix-test-stamp"; + assert_eq!(stamp.path.file_name().unwrap(), expected_filename); +} diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 8fd6c8aa23ae..85d17c8fa507 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -325,4 +325,14 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "Removed `rust.parallel-compiler` as it was deprecated in #132282 long time ago.", }, + ChangeInfo { + change_id: 135326, + severity: ChangeSeverity::Warning, + summary: "It is now possible to configure `optimized-compiler-builtins` for per target.", + }, + ChangeInfo { + change_id: 135281, + severity: ChangeSeverity::Warning, + summary: "Some stamp names in the build artifacts may have changed slightly (e.g., from `llvm-finished-building` to `.llvm-stamp`).", + }, ]; diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index 530d760a584c..60216395c2f6 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -120,7 +120,7 @@ impl BootstrapCommand { Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self } } - #[must_use] + #[allow(dead_code)] pub fn fail_fast(self) -> Self { Self { failure_behavior: BehaviorOnFailure::Exit, ..self } } @@ -275,7 +275,7 @@ impl CommandOutput { !self.is_success() } - #[must_use] + #[allow(dead_code)] pub fn status(&self) -> Option { match self.status { CommandStatus::Finished(status) => Some(status), diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 3657d9b31121..516c314024b2 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -16,6 +16,7 @@ use object::read::archive::ArchiveFile; use crate::LldMode; use crate::core::builder::Builder; use crate::core::config::{Config, TargetSelection}; +use crate::utils::exec::{BootstrapCommand, command}; pub use crate::utils::shared_helpers::{dylib_path, dylib_path_var}; #[cfg(test)] @@ -45,10 +46,8 @@ macro_rules! t { } }; } + pub use t; - -use crate::utils::exec::{BootstrapCommand, command}; - pub fn exe(name: &str, target: TargetSelection) -> String { crate::utils::shared_helpers::exe(name, &target.triple) } @@ -148,14 +147,6 @@ impl Drop for TimeIt { } } -/// Used for download caching -pub(crate) fn program_out_of_date(stamp: &Path, key: &str) -> bool { - if !stamp.exists() { - return true; - } - t!(fs::read_to_string(stamp)) != key -} - /// Symlinks two directories, using junctions on Windows and normal symlinks on /// Unix. pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<()> { @@ -181,10 +172,7 @@ pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result< /// copy and remove the file otherwise pub fn move_file, Q: AsRef>(from: P, to: Q) -> io::Result<()> { match fs::rename(&from, &to) { - // FIXME: Once `ErrorKind::CrossesDevices` is stabilized use - // if e.kind() == io::ErrorKind::CrossesDevices { - #[cfg(unix)] - Err(e) if e.raw_os_error() == Some(libc::EXDEV) => { + Err(e) if e.kind() == io::ErrorKind::CrossesDevices => { std::fs::copy(&from, &to)?; std::fs::remove_file(&from) } @@ -601,41 +589,3 @@ pub fn set_file_times>(path: P, times: fs::FileTimes) -> io::Resu }; f.set_times(times) } - -pub struct HashStamp { - pub path: PathBuf, - pub hash: Option>, -} - -impl HashStamp { - pub fn new(path: PathBuf, hash: Option<&str>) -> Self { - HashStamp { path, hash: hash.map(|s| s.as_bytes().to_owned()) } - } - - pub fn is_done(&self) -> bool { - match fs::read(&self.path) { - Ok(h) => self.hash.as_deref().unwrap_or(b"") == h.as_slice(), - Err(e) if e.kind() == io::ErrorKind::NotFound => false, - Err(e) => { - panic!("failed to read stamp file `{}`: {}", self.path.display(), e); - } - } - } - - pub fn remove(&self) -> io::Result<()> { - match fs::remove_file(&self.path) { - Ok(()) => Ok(()), - Err(e) => { - if e.kind() == io::ErrorKind::NotFound { - Ok(()) - } else { - Err(e) - } - } - } - } - - pub fn write(&self) -> io::Result<()> { - fs::write(&self.path, self.hash.as_deref().unwrap_or(b"")) - } -} diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index 613286cfaa89..9030ca2820a8 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -1,10 +1,10 @@ -use std::fs::{self, File, remove_file}; +use std::fs::{self, File}; use std::io::Write; use std::path::PathBuf; use crate::utils::helpers::{ - check_cfg_arg, extract_beta_rev, hex_encode, make, program_out_of_date, set_file_times, - submodule_path_of, symlink_dir, + check_cfg_arg, extract_beta_rev, hex_encode, make, set_file_times, submodule_path_of, + symlink_dir, }; use crate::{Config, Flags}; @@ -57,22 +57,6 @@ fn test_check_cfg_arg() { ); } -#[test] -fn test_program_out_of_date() { - let config = - Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); - let tempfile = config.tempdir().join(".tmp-stamp-file"); - File::create(&tempfile).unwrap().write_all(b"dummy value").unwrap(); - assert!(tempfile.exists()); - - // up-to-date - assert!(!program_out_of_date(&tempfile, "dummy value")); - // out-of-date - assert!(program_out_of_date(&tempfile, "")); - - remove_file(tempfile).unwrap(); -} - #[test] fn test_symlink_dir() { let config = diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs index b5f5e2ba6dc8..ea56932b4043 100644 --- a/src/bootstrap/src/utils/mod.rs +++ b/src/bootstrap/src/utils/mod.rs @@ -2,6 +2,7 @@ //! support for a wide range of tasks and operations such as caching, tarballs, release //! channels, job management, etc. +pub(crate) mod build_stamp; pub(crate) mod cache; pub(crate) mod cc_detect; pub(crate) mod change_tracker; @@ -9,10 +10,12 @@ pub(crate) mod channel; pub(crate) mod exec; pub(crate) mod helpers; pub(crate) mod job; -#[cfg(feature = "build-metrics")] -pub(crate) mod metrics; pub(crate) mod render_tests; pub(crate) mod shared_helpers; pub(crate) mod tarball; + +#[cfg(feature = "build-metrics")] +pub(crate) mod metrics; + #[cfg(test)] mod tests; 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/README.md b/src/ci/docker/README.md index 508b7b40c01e..2f52ff5a99a5 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -1,29 +1,30 @@ # Docker images for CI This folder contains a bunch of docker images used by the continuous integration -(CI) of Rust. An script is accompanied (`run.sh`) with these images to actually -execute them. To test out an image execute: +(CI) of Rust. A script is accompanied (`run.sh`) with these images to actually +execute them. + +Note that a single Docker image can be used by multiple CI jobs, so the job name +is the important thing that you should know. You can examine the existing CI jobs in +the [`jobs.yml`](../github-actions/jobs.yml) file. + +To run a specific CI job locally, you can use the following script: ``` -./src/ci/docker/run.sh $image_name +python3 ./src/ci/github-actions/ci.py run-local ``` -for example: - +For example, to run the `x86_64-gnu-llvm-18-1` job: ``` -./src/ci/docker/run.sh x86_64-gnu +python3 ./src/ci/github-actions/ci.py run-local x86_64-gnu-llvm-18-1 ``` -Images will output artifacts in an `obj/$image_name` dir at the root of a repository. Note -that the script will overwrite the contents of this directory. - -To match conditions in rusts CI, also set the environment variable `DEPLOY=1`, e.g.: -``` -DEPLOY=1 ./src/ci/docker/run.sh x86_64-gnu -``` +The job will output artifacts in an `obj/` dir at the root of a repository. Note +that the script will overwrite the contents of this directory. `` is set based on the +Docker image executed in the given CI job. **NOTE**: In CI, the script outputs the artifacts to the `obj` directory, -while locally, to the `obj/$image_name` directory. This is primarily to prevent +while locally, to the `obj/` directory. This is primarily to prevent strange linker errors when using multiple Docker images. For some Linux workflows (for example `x86_64-gnu-llvm-18-N`), the process is more involved. You will need to see which script is executed for the given workflow inside the [`jobs.yml`](../github-actions/jobs.yml) file and pass it through the `DOCKER_SCRIPT` environment variable. For example, to reproduce the `x86_64-gnu-llvm-18-3` workflow, you can run the following script: diff --git a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile new file mode 100644 index 000000000000..8b0fb0710d4d --- /dev/null +++ b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile @@ -0,0 +1,100 @@ +# We document platform support for minimum glibc 2.17 and kernel 3.2. +# CentOS 7 has headers for kernel 3.10, but that's fine as long as we don't +# actually use newer APIs in rustc or std without a fallback. It's more +# important that we match glibc for ELF symbol versioning. +FROM centos:7 + +WORKDIR /build + +# CentOS 7 EOL is June 30, 2024, but the repos remain in the vault. +RUN sed -i /etc/yum.repos.d/*.repo -e 's!^mirrorlist!#mirrorlist!' \ + -e 's!^#baseurl=http://mirror.centos.org/!baseurl=https://vault.centos.org/!' +RUN sed -i 's/enabled=1/enabled=0/' /etc/yum/pluginconf.d/fastestmirror.conf + +RUN yum upgrade -y && \ + yum install -y \ + automake \ + bzip2 \ + file \ + gcc \ + gcc-c++ \ + git \ + glibc-devel \ + libedit-devel \ + libstdc++-devel \ + make \ + ncurses-devel \ + openssl-devel \ + patch \ + perl \ + perl-core \ + pkgconfig \ + python3 \ + unzip \ + wget \ + xz \ + zlib-devel \ + && yum clean all + +RUN mkdir -p /rustroot/bin + +ENV PATH=/rustroot/bin:$PATH +ENV LD_LIBRARY_PATH=/rustroot/lib64:/rustroot/lib32:/rustroot/lib +ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig +WORKDIR /tmp +RUN mkdir /home/user +COPY scripts/shared.sh /tmp/ + +# Need at least GCC 5.1 to compile LLVM +COPY scripts/build-gcc.sh /tmp/ +ENV GCC_VERSION=9.5.0 +ENV GCC_BUILD_TARGET=aarch64-unknown-linux-gnu +RUN ./build-gcc.sh && yum remove -y gcc gcc-c++ + +ENV CC=gcc CXX=g++ + +# LLVM 17 needs cmake 3.20 or higher. +COPY scripts/cmake.sh /tmp/ +RUN ./cmake.sh + +# Build LLVM+Clang +COPY scripts/build-clang.sh /tmp/ +ENV LLVM_BUILD_TARGETS=AArch64 +RUN ./build-clang.sh +ENV CC=clang CXX=clang++ + +# Build zstd to enable `llvm.libzstd`. +COPY scripts/build-zstd.sh /tmp/ +RUN ./build-zstd.sh + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV PGO_HOST=aarch64-unknown-linux-gnu +ENV HOSTS=aarch64-unknown-linux-gnu + +ENV CPATH=/usr/include/aarch64-linux-gnu/:$CPATH + +ENV RUST_CONFIGURE_ARGS \ + --build=aarch64-unknown-linux-gnu \ + --enable-full-tools \ + --enable-profiler \ + --enable-sanitizers \ + --enable-compiler-docs \ + --set target.aarch64-unknown-linux-gnu.linker=clang \ + --set target.aarch64-unknown-linux-gnu.ar=/rustroot/bin/llvm-ar \ + --set target.aarch64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \ + --set llvm.link-shared=true \ + --set llvm.thin-lto=true \ + --set llvm.libzstd=true \ + --set llvm.ninja=false \ + --set rust.debug-assertions=false \ + --set rust.jemalloc \ + --set rust.use-lld=true \ + --set rust.codegen-units=1 + +ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS + +ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=clang +ENV LIBCURL_NO_PKG_CONFIG 1 +ENV DIST_REQUIRE_ALL_TOOLS 1 diff --git a/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile deleted file mode 100644 index 18972387e34d..000000000000 --- a/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -FROM ubuntu:22.04 - -COPY scripts/cross-apt-packages.sh /scripts/ -RUN sh /scripts/cross-apt-packages.sh - -COPY scripts/crosstool-ng.sh /scripts/ -RUN sh /scripts/crosstool-ng.sh - -COPY scripts/rustbuild-setup.sh /scripts/ -RUN sh /scripts/rustbuild-setup.sh -WORKDIR /tmp - -COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig /tmp/crosstool.defconfig -RUN /scripts/crosstool-ng-build.sh - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -ENV PATH=$PATH:/x-tools/aarch64-unknown-linux-gnu/bin - -ENV CC_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnu-gcc \ - AR_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnu-ar \ - CXX_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnu-g++ - -ENV HOSTS=aarch64-unknown-linux-gnu - -ENV RUST_CONFIGURE_ARGS \ - --enable-full-tools \ - --enable-profiler \ - --enable-sanitizers -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig deleted file mode 100644 index 520b1667c8be..000000000000 --- a/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig +++ /dev/null @@ -1,10 +0,0 @@ -CT_CONFIG_VERSION="4" -CT_PREFIX_DIR="/x-tools/${CT_TARGET}" -CT_USE_MIRROR=y -CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" -CT_ARCH_ARM=y -CT_ARCH_64=y -CT_KERNEL_LINUX=y -CT_LINUX_V_4_1=y -CT_GLIBC_V_2_17=y -CT_CC_LANG_CXX=y diff --git a/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile index 7e946df61631..ea5b208a7c1d 100644 --- a/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile @@ -19,6 +19,7 @@ RUN yum upgrade -y && \ gcc \ gcc-c++ \ git \ + binutils.i686 \ glibc-devel.i686 \ glibc-devel.x86_64 \ libedit-devel \ @@ -46,11 +47,12 @@ ENV LD_LIBRARY_PATH=/rustroot/lib64:/rustroot/lib32:/rustroot/lib ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig WORKDIR /tmp RUN mkdir /home/user -COPY host-x86_64/dist-x86_64-linux/shared.sh /tmp/ +COPY scripts/shared.sh /tmp/ # Need at least GCC 5.1 to compile LLVM nowadays -COPY host-x86_64/dist-x86_64-linux/build-gcc.sh /tmp/ +COPY scripts/build-gcc.sh /tmp/ ENV GCC_VERSION=9.5.0 +ENV GCC_BUILD_TARGET=i686-pc-linux-gnu RUN ./build-gcc.sh && yum remove -y gcc gcc-c++ COPY scripts/cmake.sh /tmp/ @@ -58,7 +60,8 @@ RUN ./cmake.sh # Now build LLVM+Clang, afterwards configuring further compilations to use the # clang/clang++ compilers. -COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/ +COPY scripts/build-clang.sh /tmp/ +ENV LLVM_BUILD_TARGETS=X86 RUN ./build-clang.sh ENV CC=clang CXX=clang++ diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile index 9ef391892497..9d3be51d037d 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile @@ -18,7 +18,7 @@ RUN /scripts/crosstool-ng-build.sh WORKDIR /build RUN apt-get install -y --no-install-recommends rpm2cpio cpio -COPY host-x86_64/dist-powerpc64le-linux/shared.sh host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh /build/ +COPY scripts/shared.sh host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh /build/ RUN ./build-powerpc64le-toolchain.sh COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh deleted file mode 100644 index dc86dddd464f..000000000000 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -hide_output() { - set +x - on_err=" -echo ERROR: An error was encountered with the build. -cat /tmp/build.log -exit 1 -" - trap "$on_err" ERR - bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & - PING_LOOP_PID=$! - "$@" &> /tmp/build.log - trap - ERR - kill $PING_LOOP_PID - set -x -} 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/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index bbb4fe216a55..dde6fe7f6d0f 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -47,11 +47,12 @@ ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig # Clang needs to access GCC headers to enable linker plugin LTO WORKDIR /tmp RUN mkdir /home/user -COPY host-x86_64/dist-x86_64-linux/shared.sh /tmp/ +COPY scripts/shared.sh /tmp/ # Need at least GCC 5.1 to compile LLVM nowadays -COPY host-x86_64/dist-x86_64-linux/build-gcc.sh /tmp/ +COPY scripts/build-gcc.sh /tmp/ ENV GCC_VERSION=9.5.0 +ENV GCC_BUILD_TARGET=x86_64-pc-linux-gnu RUN ./build-gcc.sh && yum remove -y gcc gcc-c++ # LLVM 17 needs cmake 3.20 or higher. @@ -60,12 +61,13 @@ RUN ./cmake.sh # Now build LLVM+Clang, afterwards configuring further compilations to use the # clang/clang++ compilers. -COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/ +COPY scripts/build-clang.sh /tmp/ +ENV LLVM_BUILD_TARGETS=X86 RUN ./build-clang.sh ENV CC=clang CXX=clang++ # Build zstd to enable `llvm.libzstd`. -COPY host-x86_64/dist-x86_64-linux/build-zstd.sh /tmp/ +COPY scripts/build-zstd.sh /tmp/ RUN ./build-zstd.sh COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh deleted file mode 100644 index dc86dddd464f..000000000000 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -hide_output() { - set +x - on_err=" -echo ERROR: An error was encountered with the build. -cat /tmp/build.log -exit 1 -" - trap "$on_err" ERR - bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & - PING_LOOP_PID=$! - "$@" &> /tmp/build.log - trap - ERR - kill $PING_LOOP_PID - set -x -} diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile index e157debfd09a..0a58f337d9dd 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile @@ -54,8 +54,8 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10 -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ +COPY scripts/shared.sh /scripts/ +COPY scripts/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile index e7016e7d3c0d..092847cdfe04 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile @@ -54,8 +54,8 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10 -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ +COPY scripts/shared.sh /scripts/ +COPY scripts/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index 2a09cd54b139..ab749b3fdd5a 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -89,8 +89,8 @@ ENV HOST_TARGET x86_64-unknown-linux-gnu # assertions enabled! Therefore, we cannot force download CI rustc. #ENV FORCE_CI_RUSTC 1 -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ +COPY scripts/shared.sh /scripts/ +COPY scripts/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh b/src/ci/docker/scripts/build-clang.sh similarity index 79% rename from src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh rename to src/ci/docker/scripts/build-clang.sh index 3c8123d90de0..841a0adb2ab8 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh +++ b/src/ci/docker/scripts/build-clang.sh @@ -21,6 +21,12 @@ cd clang-build # include path, /rustroot/include, to clang's default include path. INC="/rustroot/include:/usr/include" +GCC_PLUGIN_TARGET=$GCC_BUILD_TARGET +# We build gcc for the i686 job on x86_64 so the plugin will end up under an x86_64 path +if [[ $GCC_PLUGIN_TARGET == "i686-pc-linux-gnu" ]]; then + GCC_PLUGIN_TARGET=x86_64-pc-linux-gnu +fi + # We need compiler-rt for the profile runtime (used later to PGO the LLVM build) # but sanitizers aren't currently building. Since we don't need those, just # disable them. BOLT is used for optimizing LLVM. @@ -34,12 +40,12 @@ hide_output \ -DCOMPILER_RT_BUILD_XRAY=OFF \ -DCOMPILER_RT_BUILD_MEMPROF=OFF \ -DCOMPILER_RT_BUILD_CTX_PROFILE=OFF \ - -DLLVM_TARGETS_TO_BUILD=X86 \ + -DLLVM_TARGETS_TO_BUILD=$LLVM_BUILD_TARGETS \ -DLLVM_INCLUDE_BENCHMARKS=OFF \ -DLLVM_INCLUDE_TESTS=OFF \ -DLLVM_INCLUDE_EXAMPLES=OFF \ -DLLVM_ENABLE_PROJECTS="clang;lld;compiler-rt;bolt" \ - -DLLVM_BINUTILS_INCDIR="/rustroot/lib/gcc/x86_64-pc-linux-gnu/$GCC_VERSION/plugin/include/" \ + -DLLVM_BINUTILS_INCDIR="/rustroot/lib/gcc/$GCC_PLUGIN_TARGET/$GCC_VERSION/plugin/include/" \ -DC_INCLUDE_DIRS="$INC" hide_output make -j$(nproc) diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh b/src/ci/docker/scripts/build-gcc.sh similarity index 87% rename from src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh rename to src/ci/docker/scripts/build-gcc.sh index 57d4d338a506..11db5aa811ce 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh +++ b/src/ci/docker/scripts/build-gcc.sh @@ -51,7 +51,9 @@ cd .. rm -rf gcc-build rm -rf gcc-$GCC -# FIXME: clang doesn't find 32-bit libraries in /rustroot/lib, -# but it does look all the way under /rustroot/lib/[...]/32, -# so we can link stuff there to help it out. -ln /rustroot/lib/*.{a,so} -rst /rustroot/lib/gcc/x86_64-pc-linux-gnu/$GCC/32/ +if [[ $GCC_BUILD_TARGET == "i686-pc-linux-gnu" ]]; then + # FIXME: clang doesn't find 32-bit libraries in /rustroot/lib, + # but it does look all the way under /rustroot/lib/[...]/32, + # so we can link stuff there to help it out. + ln /rustroot/lib/*.{a,so} -rst /rustroot/lib/gcc/x86_64-pc-linux-gnu/$GCC/32/ +fi diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gccjit.sh b/src/ci/docker/scripts/build-gccjit.sh similarity index 100% rename from src/ci/docker/host-x86_64/dist-x86_64-linux/build-gccjit.sh rename to src/ci/docker/scripts/build-gccjit.sh diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-zstd.sh b/src/ci/docker/scripts/build-zstd.sh similarity index 100% rename from src/ci/docker/host-x86_64/dist-x86_64-linux/build-zstd.sh rename to src/ci/docker/scripts/build-zstd.sh diff --git a/src/ci/github-actions/calculate-job-matrix.py b/src/ci/github-actions/ci.py similarity index 52% rename from src/ci/github-actions/calculate-job-matrix.py rename to src/ci/github-actions/ci.py index 1f994f0ffd2e..b7dac412dbec 100755 --- a/src/ci/github-actions/calculate-job-matrix.py +++ b/src/ci/github-actions/ci.py @@ -1,18 +1,20 @@ #!/usr/bin/env python3 """ -This script serves for generating a matrix of jobs that should -be executed on CI. +This script contains CI functionality. +It can be used to generate a matrix of jobs that should +be executed on CI, or run a specific CI job locally. -It reads job definitions from `src/ci/github-actions/jobs.yml` -and filters them based on the event that happened on CI. +It reads job definitions from `src/ci/github-actions/jobs.yml`. """ +import argparse import dataclasses import json import logging import os import re +import subprocess import typing from pathlib import Path from typing import List, Dict, Any, Optional @@ -25,13 +27,19 @@ JOBS_YAML_PATH = Path(__file__).absolute().parent / "jobs.yml" Job = Dict[str, Any] -def name_jobs(jobs: List[Dict], prefix: str) -> List[Job]: +def add_job_properties(jobs: List[Dict], prefix: str) -> List[Job]: """ - Add a `name` attribute to each job, based on its image and the given `prefix`. + Modify the `name` attribute of each job, based on its base name and the given `prefix`. + Add an `image` attribute to each job, based on its image. """ + modified_jobs = [] for job in jobs: - job["name"] = f"{prefix} - {job['image']}" - return jobs + # Create a copy of the `job` dictionary to avoid modifying `jobs` + new_job = dict(job) + new_job["image"] = get_job_image(new_job) + new_job["full_name"] = f"{prefix} - {new_job['name']}" + modified_jobs.append(new_job) + return modified_jobs def add_base_env(jobs: List[Job], environment: Dict[str, str]) -> List[Job]: @@ -39,11 +47,15 @@ def add_base_env(jobs: List[Job], environment: Dict[str, str]) -> List[Job]: Prepends `environment` to the `env` attribute of each job. The `env` of each job has higher precedence than `environment`. """ + modified_jobs = [] for job in jobs: env = environment.copy() env.update(job.get("env", {})) - job["env"] = env - return jobs + + new_job = dict(job) + new_job["env"] = env + modified_jobs.append(new_job) + return modified_jobs @dataclasses.dataclass @@ -116,7 +128,9 @@ def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]: def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[Job]: if isinstance(run_type, PRRunType): - return add_base_env(name_jobs(job_data["pr"], "PR"), job_data["envs"]["pr"]) + return add_base_env( + add_job_properties(job_data["pr"], "PR"), job_data["envs"]["pr"] + ) elif isinstance(run_type, TryRunType): jobs = job_data["try"] custom_jobs = run_type.custom_jobs @@ -130,7 +144,7 @@ def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[ jobs = [] unknown_jobs = [] for custom_job in custom_jobs: - job = [j for j in job_data["auto"] if j["image"] == custom_job] + job = [j for j in job_data["auto"] if j["name"] == custom_job] if not job: unknown_jobs.append(custom_job) continue @@ -140,10 +154,10 @@ def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[ f"Custom job(s) `{unknown_jobs}` not found in auto jobs" ) - return add_base_env(name_jobs(jobs, "try"), job_data["envs"]["try"]) + return add_base_env(add_job_properties(jobs, "try"), job_data["envs"]["try"]) elif isinstance(run_type, AutoRunType): return add_base_env( - name_jobs(job_data["auto"], "auto"), job_data["envs"]["auto"] + add_job_properties(job_data["auto"], "auto"), job_data["envs"]["auto"] ) return [] @@ -181,12 +195,64 @@ def format_run_type(run_type: WorkflowRunType) -> str: raise AssertionError() -if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) +def get_job_image(job: Job) -> str: + """ + By default, the Docker image of a job is based on its name. + However, it can be overridden by its IMAGE environment variable. + """ + env = job.get("env", {}) + # Return the IMAGE environment variable if it exists, otherwise return the job name + return env.get("IMAGE", job["name"]) - with open(JOBS_YAML_PATH) as f: - data = yaml.safe_load(f) +def is_linux_job(job: Job) -> bool: + return "ubuntu" in job["os"] + + +def find_linux_job(job_data: Dict[str, Any], job_name: str, pr_jobs: bool) -> Job: + candidates = job_data["pr"] if pr_jobs else job_data["auto"] + jobs = [job for job in candidates if job.get("name") == job_name] + if len(jobs) == 0: + available_jobs = "\n".join( + sorted(job["name"] for job in candidates if is_linux_job(job)) + ) + raise Exception(f"""Job `{job_name}` not found in {'pr' if pr_jobs else 'auto'} jobs. +The following jobs are available: +{available_jobs}""") + assert len(jobs) == 1 + + job = jobs[0] + if not is_linux_job(job): + raise Exception("Only Linux jobs can be executed locally") + return job + + +def run_workflow_locally(job_data: Dict[str, Any], job_name: str, pr_jobs: bool): + DOCKER_DIR = Path(__file__).absolute().parent.parent / "docker" + + job = find_linux_job(job_data, job_name=job_name, pr_jobs=pr_jobs) + + custom_env = {} + # Replicate src/ci/scripts/setup-environment.sh + # Adds custom environment variables to the job + if job_name.startswith("dist-"): + if job_name.endswith("-alt"): + custom_env["DEPLOY_ALT"] = "1" + else: + custom_env["DEPLOY"] = "1" + custom_env.update({k: str(v) for (k, v) in job.get("env", {}).items()}) + + args = [str(DOCKER_DIR / "run.sh"), get_job_image(job)] + env_formatted = [f"{k}={v}" for (k, v) in sorted(custom_env.items())] + print(f"Executing `{' '.join(env_formatted)} {' '.join(args)}`") + + env = os.environ.copy() + env.update(custom_env) + + subprocess.run(args, env=env) + + +def calculate_job_matrix(job_data: Dict[str, Any]): github_ctx = get_github_ctx() run_type = find_run_type(github_ctx) @@ -197,7 +263,7 @@ if __name__ == "__main__": jobs = [] if run_type is not None: - jobs = calculate_jobs(run_type, data) + jobs = calculate_jobs(run_type, job_data) jobs = skip_jobs(jobs, channel) if not jobs: @@ -208,3 +274,45 @@ if __name__ == "__main__": logging.info(f"Output:\n{yaml.dump(dict(jobs=jobs, run_type=run_type), indent=4)}") print(f"jobs={json.dumps(jobs)}") print(f"run_type={run_type}") + + +def create_cli_parser(): + parser = argparse.ArgumentParser( + prog="ci.py", description="Generate or run CI workflows" + ) + subparsers = parser.add_subparsers( + help="Command to execute", dest="command", required=True + ) + subparsers.add_parser( + "calculate-job-matrix", + help="Generate a matrix of jobs that should be executed in CI", + ) + run_parser = subparsers.add_parser( + "run-local", help="Run a CI jobs locally (on Linux)" + ) + run_parser.add_argument( + "job_name", + help="CI job that should be executed. By default, a merge (auto) " + "job with the given name will be executed", + ) + run_parser.add_argument( + "--pr", action="store_true", help="Run a PR job instead of an auto job" + ) + return parser + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + with open(JOBS_YAML_PATH) as f: + data = yaml.safe_load(f) + + parser = create_cli_parser() + args = parser.parse_args() + + if args.command == "calculate-job-matrix": + calculate_job_matrix(data) + elif args.command == "run-local": + run_workflow_locally(data, args.job_name, args.pr) + else: + raise Exception(f"Unknown command {args.command}") diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 876a77935921..6bf4a75d080c 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -91,26 +91,26 @@ envs: # These jobs automatically inherit envs.pr, to avoid repeating # it in each job definition. pr: - - image: mingw-check + - name: mingw-check <<: *job-linux-4c - - image: mingw-check-tidy + - name: mingw-check-tidy continue_on_error: true <<: *job-linux-4c - - image: x86_64-gnu-llvm-18 + - name: x86_64-gnu-llvm-18 env: ENABLE_GCC_CODEGEN: "1" # We are adding (temporarily) a dummy commit on the compiler READ_ONLY_SRC: "0" DOCKER_SCRIPT: x86_64-gnu-llvm.sh <<: *job-linux-16c - - image: x86_64-gnu-tools + - name: x86_64-gnu-tools <<: *job-linux-16c # Jobs that run when you perform a try build (@bors try) # These jobs automatically inherit envs.try, to avoid repeating # it in each job definition. try: - - image: dist-x86_64-linux + - name: dist-x86_64-linux env: CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-16c @@ -123,106 +123,106 @@ auto: # Linux/Docker builders # ############################# - - image: aarch64-gnu + - name: aarch64-gnu <<: *job-aarch64-linux - - image: aarch64-gnu-debug + - name: aarch64-gnu-debug <<: *job-aarch64-linux - - image: arm-android + - name: arm-android <<: *job-linux-4c - - image: armhf-gnu + - name: armhf-gnu <<: *job-linux-4c - - image: dist-aarch64-linux + - name: dist-aarch64-linux env: CODEGEN_BACKENDS: llvm,cranelift + <<: *job-aarch64-linux + + - name: dist-android <<: *job-linux-4c - - image: dist-android - <<: *job-linux-4c - - - image: dist-arm-linux + - name: dist-arm-linux <<: *job-linux-8c - - image: dist-armhf-linux + - name: dist-armhf-linux <<: *job-linux-4c - - image: dist-armv7-linux + - name: dist-armv7-linux <<: *job-linux-4c - - image: dist-i586-gnu-i586-i686-musl + - name: dist-i586-gnu-i586-i686-musl <<: *job-linux-4c - - image: dist-i686-linux + - name: dist-i686-linux <<: *job-linux-4c - - image: dist-loongarch64-linux + - name: dist-loongarch64-linux <<: *job-linux-4c - - image: dist-loongarch64-musl + - name: dist-loongarch64-musl <<: *job-linux-4c - - image: dist-ohos + - name: dist-ohos <<: *job-linux-4c - - image: dist-powerpc-linux + - name: dist-powerpc-linux <<: *job-linux-4c - - image: dist-powerpc64-linux + - name: dist-powerpc64-linux <<: *job-linux-4c - - image: dist-powerpc64le-linux + - name: dist-powerpc64le-linux <<: *job-linux-4c-largedisk - - image: dist-riscv64-linux + - name: dist-riscv64-linux <<: *job-linux-4c - - image: dist-s390x-linux + - name: dist-s390x-linux <<: *job-linux-4c - - image: dist-various-1 + - name: dist-various-1 <<: *job-linux-4c - - image: dist-various-2 + - name: dist-various-2 <<: *job-linux-4c - - image: dist-x86_64-freebsd + - name: dist-x86_64-freebsd <<: *job-linux-4c - - image: dist-x86_64-illumos + - name: dist-x86_64-illumos <<: *job-linux-4c - - image: dist-x86_64-linux + - name: dist-x86_64-linux env: CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-16c - - image: dist-x86_64-linux-alt + - name: dist-x86_64-linux-alt env: IMAGE: dist-x86_64-linux CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-16c - - image: dist-x86_64-musl + - name: dist-x86_64-musl env: CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-4c - - image: dist-x86_64-netbsd + - name: dist-x86_64-netbsd <<: *job-linux-4c # The i686-gnu job is split into multiple jobs to run tests in parallel. # i686-gnu-1 skips tests that run in i686-gnu-2. - - image: i686-gnu-1 + - name: i686-gnu-1 env: IMAGE: i686-gnu DOCKER_SCRIPT: stage_2_test_set1.sh <<: *job-linux-4c # Skip tests that run in i686-gnu-1 - - image: i686-gnu-2 + - name: i686-gnu-2 env: IMAGE: i686-gnu DOCKER_SCRIPT: stage_2_test_set2.sh @@ -230,14 +230,14 @@ auto: # The i686-gnu-nopt job is split into multiple jobs to run tests in parallel. # i686-gnu-nopt-1 skips tests that run in i686-gnu-nopt-2 - - image: i686-gnu-nopt-1 + - name: i686-gnu-nopt-1 env: IMAGE: i686-gnu-nopt DOCKER_SCRIPT: /scripts/stage_2_test_set1.sh <<: *job-linux-4c # Skip tests that run in i686-gnu-nopt-1 - - image: i686-gnu-nopt-2 + - name: i686-gnu-nopt-2 env: IMAGE: i686-gnu-nopt DOCKER_SCRIPT: >- @@ -245,13 +245,13 @@ auto: /scripts/stage_2_test_set2.sh <<: *job-linux-4c - - image: mingw-check + - name: mingw-check <<: *job-linux-4c - - image: test-various + - name: test-various <<: *job-linux-4c - - image: x86_64-fuchsia + - name: x86_64-fuchsia # Only run this job on the nightly channel. Fuchsia requires # nightly features to compile, and this job would fail if # executed on beta and stable. @@ -260,10 +260,10 @@ auto: # Tests integration with Rust for Linux. # Builds stage 1 compiler and tries to compile a few RfL examples with it. - - image: x86_64-rust-for-linux + - name: x86_64-rust-for-linux <<: *job-linux-4c - - image: x86_64-gnu + - name: x86_64-gnu <<: *job-linux-4c # This job ensures commits landing on nightly still pass the full @@ -271,7 +271,7 @@ auto: # depend on the channel being built (for example if they include the # channel name on the output), and this builder prevents landing # changes that would result in broken builds after a promotion. - - image: x86_64-gnu-stable + - name: x86_64-gnu-stable # Only run this job on the nightly channel. Running this on beta # could cause failures when `dev: 1` in `stage0.txt`, and running # this on stable is useless. @@ -281,20 +281,20 @@ auto: RUST_CI_OVERRIDE_RELEASE_CHANNEL: stable <<: *job-linux-4c - - image: x86_64-gnu-aux + - name: x86_64-gnu-aux <<: *job-linux-4c - - image: x86_64-gnu-debug + - name: x86_64-gnu-debug # This seems to be needed because a full stage 2 build + run-make tests # overwhelms the storage capacity of the standard 4c runner. <<: *job-linux-4c-largedisk - - image: x86_64-gnu-distcheck + - name: x86_64-gnu-distcheck <<: *job-linux-8c # The x86_64-gnu-llvm-19 job is split into multiple jobs to run tests in parallel. # x86_64-gnu-llvm-19-1 skips tests that run in x86_64-gnu-llvm-19-{2,3}. - - image: x86_64-gnu-llvm-19-1 + - name: x86_64-gnu-llvm-19-1 env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-19 @@ -302,7 +302,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-19-{1,3} - - image: x86_64-gnu-llvm-19-2 + - name: x86_64-gnu-llvm-19-2 env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-19 @@ -310,7 +310,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-19-{1,2} - - image: x86_64-gnu-llvm-19-3 + - name: x86_64-gnu-llvm-19-3 env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-19 @@ -319,7 +319,7 @@ auto: # The x86_64-gnu-llvm-18 job is split into multiple jobs to run tests in parallel. # x86_64-gnu-llvm-18-1 skips tests that run in x86_64-gnu-llvm-18-{2,3}. - - image: x86_64-gnu-llvm-18-1 + - name: x86_64-gnu-llvm-18-1 env: RUST_BACKTRACE: 1 READ_ONLY_SRC: "0" @@ -328,7 +328,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-18-{1,3} - - image: x86_64-gnu-llvm-18-2 + - name: x86_64-gnu-llvm-18-2 env: RUST_BACKTRACE: 1 READ_ONLY_SRC: "0" @@ -337,7 +337,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-18-{1,2} - - image: x86_64-gnu-llvm-18-3 + - name: x86_64-gnu-llvm-18-3 env: RUST_BACKTRACE: 1 READ_ONLY_SRC: "0" @@ -345,10 +345,10 @@ auto: DOCKER_SCRIPT: x86_64-gnu-llvm3.sh <<: *job-linux-4c - - image: x86_64-gnu-nopt + - name: x86_64-gnu-nopt <<: *job-linux-4c - - image: x86_64-gnu-tools + - name: x86_64-gnu-tools env: DEPLOY_TOOLSTATES_JSON: toolstates-linux.json <<: *job-linux-4c @@ -357,7 +357,7 @@ auto: # macOS Builders # #################### - - image: dist-x86_64-apple + - name: dist-x86_64-apple env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin --set rust.codegen-units=1 @@ -371,7 +371,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-macos-xl - - image: dist-apple-various + - name: dist-apple-various env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host='' --target=aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim,aarch64-apple-ios-macabi,x86_64-apple-ios-macabi # Mac Catalyst cannot currently compile the sanitizer: @@ -385,19 +385,19 @@ auto: NO_OVERFLOW_CHECKS: 1 <<: *job-macos-xl - - image: x86_64-apple-1 + - name: x86_64-apple-1 env: <<: *env-x86_64-apple-tests <<: *job-macos-xl - - image: x86_64-apple-2 + - name: x86_64-apple-2 env: SCRIPT: ./x.py --stage 2 test tests/ui tests/rustdoc <<: *env-x86_64-apple-tests <<: *job-macos-xl # This target only needs to support 11.0 and up as nothing else supports the hardware - - image: dist-aarch64-apple + - name: dist-aarch64-apple env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin RUST_CONFIGURE_ARGS: >- @@ -421,7 +421,7 @@ auto: <<: *job-macos-m1 # This target only needs to support 11.0 and up as nothing else supports the hardware - - image: aarch64-apple + - name: aarch64-apple env: SCRIPT: ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin RUST_CONFIGURE_ARGS: >- @@ -442,20 +442,20 @@ auto: # Windows Builders # ###################### - - image: x86_64-msvc + - name: x86_64-msvc env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler SCRIPT: make ci-msvc <<: *job-windows-8c - - image: i686-msvc + - name: i686-msvc env: RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc SCRIPT: make ci-msvc <<: *job-windows-8c # x86_64-msvc-ext is split into multiple jobs to run tests in parallel. - - image: x86_64-msvc-ext1 + - name: x86_64-msvc-ext1 env: SCRIPT: python x.py --stage 2 test src/tools/cargotest src/tools/cargo RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld @@ -464,7 +464,7 @@ auto: # Temporary builder to workaround CI issues # See #FIXME: Remove this, and re-enable the same tests in `checktools.sh`, once CI issues are fixed. - - image: x86_64-msvc-ext2 + - name: x86_64-msvc-ext2 env: SCRIPT: > python x.py test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass && @@ -476,7 +476,7 @@ auto: <<: *job-windows # Run `checktools.sh` and upload the toolstate file. - - image: x86_64-msvc-ext3 + - name: x86_64-msvc-ext3 env: SCRIPT: src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh x.py /tmp/toolstate/toolstates.json windows HOST_TARGET: x86_64-pc-windows-msvc @@ -500,7 +500,7 @@ auto: # came from the mingw-w64 SourceForge download site. Unfortunately # SourceForge is notoriously flaky, so we mirror it on our own infrastructure. - - image: i686-mingw + - name: i686-mingw env: RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu SCRIPT: make ci-mingw @@ -510,7 +510,7 @@ auto: <<: *job-windows-8c # x86_64-mingw is split into two jobs to run tests in parallel. - - image: x86_64-mingw-1 + - name: x86_64-mingw-1 env: SCRIPT: make ci-mingw-x RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu @@ -519,7 +519,7 @@ auto: NO_DOWNLOAD_CI_LLVM: 1 <<: *job-windows - - image: x86_64-mingw-2 + - name: x86_64-mingw-2 env: SCRIPT: make ci-mingw-bootstrap RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu @@ -528,7 +528,7 @@ auto: NO_DOWNLOAD_CI_LLVM: 1 <<: *job-windows - - image: dist-x86_64-msvc + - name: dist-x86_64-msvc env: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-msvc @@ -542,7 +542,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows-8c - - image: dist-i686-msvc + - name: dist-i686-msvc env: RUST_CONFIGURE_ARGS: >- --build=i686-pc-windows-msvc @@ -555,7 +555,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows - - image: dist-aarch64-msvc + - name: dist-aarch64-msvc env: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-msvc @@ -567,7 +567,7 @@ auto: DIST_REQUIRE_ALL_TOOLS: 1 <<: *job-windows - - image: dist-i686-mingw + - name: dist-i686-mingw env: RUST_CONFIGURE_ARGS: >- --build=i686-pc-windows-gnu @@ -580,7 +580,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows - - image: dist-x86_64-mingw + - name: dist-x86_64-mingw env: SCRIPT: python x.py dist bootstrap --include-default-paths RUST_CONFIGURE_ARGS: >- @@ -593,7 +593,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows - - image: dist-x86_64-msvc-alt + - name: dist-x86_64-msvc-alt env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler SCRIPT: python x.py dist bootstrap --include-default-paths diff --git a/src/doc/rustc-dev-guide/README.md b/src/doc/rustc-dev-guide/README.md index 2a686b5471c6..7ce7d4ddf3e7 100644 --- a/src/doc/rustc-dev-guide/README.md +++ b/src/doc/rustc-dev-guide/README.md @@ -70,46 +70,21 @@ $ ENABLE_LINKCHECK=1 mdbook serve We use `mdbook-toc` to auto-generate TOCs for long sections. You can invoke the preprocessor by including the `` marker at the place where you want the TOC. -## How to fix toolstate failures +## Synchronizing josh subtree with rustc -> [!NOTE] -> Currently, we do not track the rustc-dev-guide toolstate due to -> [spurious failures](https://github.com/rust-lang/rust/pull/71731), -> but we leave these instructions for when we do it again in the future. +This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.github.io/josh/intro.html) subtree. You can use the following commands to synchronize the subtree in both directions. -1. You will get a ping from the toolstate commit. e.g. https://github.com/rust-lang-nursery/rust-toolstate/commit/8ffa0e4c30ac9ba8546b7046e5c4ccc2b96ebdd4 +### Pull changes from `rust-lang/rust` into this repository +1) Checkout a new branch that will be used to create a PR into `rust-lang/rustc-dev-guide` +2) Run the pull command + ``` + $ cargo run --manifest-path josh-sync/Cargo.toml rustc-pull + ``` +3) Push the branch to your fork and create a PR into `rustc-dev-guide` -2. The commit contains a link to the PR that caused the breakage. e.g. https://github.com/rust-lang/rust/pull/64321 - -3. If you go to that PR's thread, there is a post from bors with a link to the CI status: https://github.com/rust-lang/rust/pull/64321#issuecomment-529763807 - -4. Follow the check-actions link to get to the Actions page for that build - -5. There will be approximately 1 billion different jobs for the build. They are for different configurations and platforms. The rustc-dev-guide build only runs on the Linux x86_64-gnu-tools job. So click on that job in the list, which is about 60% down in the list. - -6. Click the Run build step in the job to get the console log for the step. - -7. Click on the log and Ctrl-f to get a search box in the log - -8. Search for rustc-dev-guide. This gets you to the place where the links are checked. It is usually ~11K lines into the log. - -9. Look at the links in the log near that point in the log - -10. Fix those links in the rustc-dev-guide (by making a PR in the rustc-dev-guide repo) - -11. Make a PR on the rust-lang/rust repo to update the rustc-dev-guide git submodule in src/docs/rustc-dev-guide. -To make a PR, the following steps are useful. - -```bash -# Assuming you already cloned the rust-lang/rust repo and you're in the correct directory -git submodule update --remote src/doc/rustc-dev-guide -git add -u -git commit -m "Update rustc-dev-guide" -# Note that you can use -i, which is short for --incremental, in the following command -./x test --incremental src/doc/rustc-dev-guide # This is optional and should succeed anyway -# Open a PR in rust-lang/rust -``` - -12. Wait for PR to merge - -Voilà! +### Push changes from this repository into `rust-lang/rust` +1) Run the push command to create a branch named `` in a `rustc` fork under the `` account + ``` + $ cargo run --manifest-path josh-sync/Cargo.toml rustc-push + ``` +2) Create a PR from `` into `rust-lang/rust` diff --git a/src/doc/rustc-dev-guide/josh-sync/Cargo.lock b/src/doc/rustc-dev-guide/josh-sync/Cargo.lock new file mode 100644 index 000000000000..844518628c43 --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/Cargo.lock @@ -0,0 +1,430 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "josh-sync" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "directories", + "xshell", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "xshell" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e7290c623014758632efe00737145b6867b66292c42167f2ec381eb566a373d" +dependencies = [ + "xshell-macros", +] + +[[package]] +name = "xshell-macros" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547" diff --git a/src/doc/rustc-dev-guide/josh-sync/Cargo.toml b/src/doc/rustc-dev-guide/josh-sync/Cargo.toml new file mode 100644 index 000000000000..81d0d1ebd22d --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "josh-sync" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.95" +clap = { version = "4.5.21", features = ["derive"] } +directories = "5" +xshell = "0.2.6" diff --git a/src/doc/rustc-dev-guide/josh-sync/README.md b/src/doc/rustc-dev-guide/josh-sync/README.md new file mode 100644 index 000000000000..a3dd876e8b87 --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/README.md @@ -0,0 +1,4 @@ +# Git josh sync +This utility serves for syncing the josh git subtree to and from the rust-lang/rust repository. + +See CLI help for usage. diff --git a/src/doc/rustc-dev-guide/josh-sync/src/main.rs b/src/doc/rustc-dev-guide/josh-sync/src/main.rs new file mode 100644 index 000000000000..84613ad86890 --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/src/main.rs @@ -0,0 +1,32 @@ +use clap::Parser; +use crate::sync::GitSync; + +mod sync; + +#[derive(clap::Parser)] +enum Args { + /// Pull changes from the main `rustc` repository. + /// This creates new commits that should be then merged into `rustc-dev-guide`. + RustcPull, + /// Push changes from `rustc-dev-guide` to the given `branch` of a `rustc` fork under the given + /// GitHub `username`. + /// The pushed branch should then be merged into the `rustc` repository. + RustcPush { + branch: String, + github_username: String + } +} + +fn main() -> anyhow::Result<()> { + let args = Args::parse(); + let sync = GitSync::from_current_dir()?; + match args { + Args::RustcPull => { + sync.rustc_pull(None)?; + } + Args::RustcPush { github_username, branch } => { + sync.rustc_push(github_username, branch)?; + } + } + Ok(()) +} diff --git a/src/doc/rustc-dev-guide/josh-sync/src/sync.rs b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs new file mode 100644 index 000000000000..da21a4c9a27c --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs @@ -0,0 +1,234 @@ +use std::ops::Not; +use std::path::PathBuf; +use std::{env, net, process}; +use std::io::Write; +use std::time::Duration; +use anyhow::{anyhow, bail, Context}; +use xshell::{cmd, Shell}; + +/// Used for rustc syncs. +const JOSH_FILTER: &str = ":/src/doc/rustc-dev-guide"; +const JOSH_PORT: u16 = 42042; +const UPSTREAM_REPO: &str = "rust-lang/rust"; + +pub struct GitSync { + dir: PathBuf, +} + +/// This code was adapted from the miri repository +/// (https://github.com/rust-lang/miri/blob/6a68a79f38064c3bc30617cca4bdbfb2c336b140/miri-script/src/commands.rs#L236). +impl GitSync { + pub fn from_current_dir() -> anyhow::Result { + Ok(Self { + dir: std::env::current_dir()? + }) + } + + pub fn rustc_pull(&self, commit: Option) -> anyhow::Result<()> { + let sh = Shell::new()?; + sh.change_dir(&self.dir); + let commit = commit.map(Ok).unwrap_or_else(|| { + let rust_repo_head = + cmd!(sh, "git ls-remote https://github.com/{UPSTREAM_REPO}/ HEAD").read()?; + rust_repo_head + .split_whitespace() + .next() + .map(|front| front.trim().to_owned()) + .ok_or_else(|| anyhow!("Could not obtain Rust repo HEAD from remote.")) + })?; + // Make sure the repo is clean. + if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { + bail!("working directory must be clean before performing rustc pull"); + } + // Make sure josh is running. + let josh = Self::start_josh()?; + let josh_url = + format!("http://localhost:{JOSH_PORT}/{UPSTREAM_REPO}.git@{commit}{JOSH_FILTER}.git"); + + // Update rust-version file. As a separate commit, since making it part of + // the merge has confused the heck out of josh in the past. + // We pass `--no-verify` to avoid running git hooks. + // We do this before the merge so that if there are merge conflicts, we have + // the right rust-version file while resolving them. + sh.write_file("rust-version", format!("{commit}\n"))?; + const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rustc"; + cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}") + .run() + .context("FAILED to commit rust-version file, something went wrong")?; + + // Fetch given rustc commit. + cmd!(sh, "git fetch {josh_url}") + .run() + .inspect_err(|_| { + // Try to un-do the previous `git commit`, to leave the repo in the state we found it. + cmd!(sh, "git reset --hard HEAD^") + .run() + .expect("FAILED to clean up again after failed `git fetch`, sorry for that"); + }) + .context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?; + + // This should not add any new root commits. So count those before and after merging. + let num_roots = || -> anyhow::Result { + Ok(cmd!(sh, "git rev-list HEAD --max-parents=0 --count") + .read() + .context("failed to determine the number of root commits")? + .parse::()?) + }; + let num_roots_before = num_roots()?; + + // Merge the fetched commit. + const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc"; + cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}") + .run() + .context("FAILED to merge new commits, something went wrong")?; + + // Check that the number of roots did not increase. + if num_roots()? != num_roots_before { + bail!("Josh created a new root commit. This is probably not the history you want."); + } + + drop(josh); + Ok(()) + } + + pub fn rustc_push(&self, github_user: String, branch: String) -> anyhow::Result<()> { + let sh = Shell::new()?; + sh.change_dir(&self.dir); + let base = sh.read_file("rust-version")?.trim().to_owned(); + // Make sure the repo is clean. + if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { + bail!("working directory must be clean before running `rustc-push`"); + } + // Make sure josh is running. + let josh = Self::start_josh()?; + let josh_url = + format!("http://localhost:{JOSH_PORT}/{github_user}/rust.git{JOSH_FILTER}.git"); + + // Find a repo we can do our preparation in. + if let Ok(rustc_git) = env::var("RUSTC_GIT") { + // If rustc_git is `Some`, we'll use an existing fork for the branch updates. + sh.change_dir(rustc_git); + } else { + // Otherwise, do this in the local repo. + println!( + "This will pull a copy of the rust-lang/rust history into this checkout, growing it by about 1GB." + ); + print!( + "To avoid that, abort now and set the `RUSTC_GIT` environment variable to an existing rustc checkout. Proceed? [y/N] " + ); + std::io::stdout().flush()?; + let mut answer = String::new(); + std::io::stdin().read_line(&mut answer)?; + if answer.trim().to_lowercase() != "y" { + std::process::exit(1); + } + }; + // Prepare the branch. Pushing works much better if we use as base exactly + // the commit that we pulled from last time, so we use the `rust-version` + // file to find out which commit that would be. + println!("Preparing {github_user}/rust (base: {base})..."); + if cmd!(sh, "git fetch https://github.com/{github_user}/rust {branch}") + .ignore_stderr() + .read() + .is_ok() + { + println!( + "The branch '{branch}' seems to already exist in 'https://github.com/{github_user}/rust'. Please delete it and try again." + ); + std::process::exit(1); + } + cmd!(sh, "git fetch https://github.com/{UPSTREAM_REPO} {base}").run()?; + cmd!(sh, "git push https://github.com/{github_user}/rust {base}:refs/heads/{branch}") + .ignore_stdout() + .ignore_stderr() // silence the "create GitHub PR" message + .run()?; + println!(); + + // Do the actual push. + sh.change_dir(&self.dir); + println!("Pushing changes..."); + cmd!(sh, "git push {josh_url} HEAD:{branch}").run()?; + println!(); + + // Do a round-trip check to make sure the push worked as expected. + cmd!(sh, "git fetch {josh_url} {branch}").ignore_stderr().read()?; + let head = cmd!(sh, "git rev-parse HEAD").read()?; + let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?; + if head != fetch_head { + bail!( + "Josh created a non-roundtrip push! Do NOT merge this into rustc!\n\ + Expected {head}, got {fetch_head}." + ); + } + println!( + "Confirmed that the push round-trips back to rustc-dev-guide properly. Please create a rustc PR:" + ); + println!( + // Open PR with `subtree update` title to silence the `no-merges` triagebot check + " https://github.com/{UPSTREAM_REPO}/compare/{github_user}:{branch}?quick_pull=1&title=Rustc+dev+guide+subtree+update&body=r?+@ghost" + ); + + drop(josh); + Ok(()) + } + + fn start_josh() -> anyhow::Result { + // Determine cache directory. + let local_dir = { + let user_dirs = + directories::ProjectDirs::from("org", "rust-lang", "rustc-dev-guide-josh").unwrap(); + user_dirs.cache_dir().to_owned() + }; + + // Start josh, silencing its output. + let mut cmd = process::Command::new("josh-proxy"); + cmd.arg("--local").arg(local_dir); + cmd.arg("--remote").arg("https://github.com"); + cmd.arg("--port").arg(JOSH_PORT.to_string()); + cmd.arg("--no-background"); + cmd.stdout(process::Stdio::null()); + cmd.stderr(process::Stdio::null()); + let josh = cmd.spawn().context("failed to start josh-proxy, make sure it is installed")?; + + // Create a wrapper that stops it on drop. + struct Josh(process::Child); + impl Drop for Josh { + fn drop(&mut self) { + #[cfg(unix)] + { + // Try to gracefully shut it down. + process::Command::new("kill") + .args(["-s", "INT", &self.0.id().to_string()]) + .output() + .expect("failed to SIGINT josh-proxy"); + // Sadly there is no "wait with timeout"... so we just give it some time to finish. + std::thread::sleep(Duration::from_millis(100)); + // Now hopefully it is gone. + if self.0.try_wait().expect("failed to wait for josh-proxy").is_some() { + return; + } + } + // If that didn't work (or we're not on Unix), kill it hard. + eprintln!( + "I have to kill josh-proxy the hard way, let's hope this does not break anything." + ); + self.0.kill().expect("failed to SIGKILL josh-proxy"); + } + } + + // Wait until the port is open. We try every 10ms until 1s passed. + for _ in 0..100 { + // This will generally fail immediately when the port is still closed. + let josh_ready = net::TcpStream::connect_timeout( + &net::SocketAddr::from(([127, 0, 0, 1], JOSH_PORT)), + Duration::from_millis(1), + ); + if josh_ready.is_ok() { + return Ok(Josh(josh)); + } + // Not ready yet. + std::thread::sleep(Duration::from_millis(10)); + } + bail!("Even after waiting for 1s, josh-proxy is still not available.") + } +} diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version new file mode 100644 index 000000000000..876c32dcf434 --- /dev/null +++ b/src/doc/rustc-dev-guide/rust-version @@ -0,0 +1 @@ +dcfa38fe234de9304169afc6638e81d0dd222c06 diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index 2fbbb187d6b3..a8fddbc7562d 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -134,14 +134,13 @@ - [Prologue](./part-4-intro.md) - [Generic parameter definitions](./generic_parameters_summary.md) - - [Implementation nuances of early/late bound parameters](./early-late-bound-params/early-late-bound-implementation-nuances.md) - - [Interactions with turbofishing](./early-late-bound-params/turbofishing-and-early-late-bound.md) + - [`EarlyBinder` and instantiating parameters](./ty_module/early_binder.md) +- [Binders and Higher ranked regions](./ty_module/binders.md) + - [Instantiating binders](./ty_module/instantiating_binders.md) +- [Early vs Late bound parameters](./early_late_parameters.md) - [The `ty` module: representing types](./ty.md) - [ADTs and Generic Arguments](./ty_module/generic_arguments.md) - [Parameter types/consts/regions](./ty_module/param_ty_const_regions.md) - - [`EarlyBinder` and instantiating parameters](./ty_module/early_binder.md) - - [`Binder` and Higher ranked regions](./ty_module/binders.md) - - [Instantiating binders](./ty_module/instantiating_binders.md) - [`TypeFolder` and `TypeFoldable`](./ty-fold.md) - [Parameter Environments](./param_env/param_env_summary.md) - [What is it?](./param_env/param_env_what_is_it.md) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index 275a1777a996..805291017c27 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -56,7 +56,7 @@ These tools include: By default, the Rust build system does not check for changes to the LLVM source code or its build configuration settings. So, if you need to rebuild the LLVM that is linked -into `rustc`, first delete the file `llvm-finished-building`, which should be located +into `rustc`, first delete the file `.llvm-stamp`, which should be located in `build//llvm/`. The default rustc compilation pipeline has multiple codegen units, which is diff --git a/src/doc/rustc-dev-guide/src/building/optimized-build.md b/src/doc/rustc-dev-guide/src/building/optimized-build.md index 3080dc6bf5d3..8feda59829b4 100644 --- a/src/doc/rustc-dev-guide/src/building/optimized-build.md +++ b/src/doc/rustc-dev-guide/src/building/optimized-build.md @@ -126,4 +126,4 @@ Here is an example of how can `opt-dist` be used locally (outside of CI): [`Environment`]: https://github.com/rust-lang/rust/blob/ee451f8faccf3050c76cdcd82543c917b40c7962/src/tools/opt-dist/src/environment.rs#L5 > Note: if you want to run the actual CI pipeline, instead of running `opt-dist` locally, -> you can execute `DEPLOY=1 src/ci/docker/run.sh dist-x86_64-linux`. +> you can execute `python3 src/ci/github-actions/ci.py run-local dist-x86_64-linux`. diff --git a/src/doc/rustc-dev-guide/src/early-late-bound-params/early-late-bound-implementation-nuances.md b/src/doc/rustc-dev-guide/src/early-late-bound-params/early-late-bound-implementation-nuances.md deleted file mode 100644 index 01569e8dc6a0..000000000000 --- a/src/doc/rustc-dev-guide/src/early-late-bound-params/early-late-bound-implementation-nuances.md +++ /dev/null @@ -1,197 +0,0 @@ -# Early and Late Bound Parameter Implementation Nuances - -> Note: this chapter makes reference to information discussed later on in the [representing types][ch_representing_types] chapter. Specifically, it uses concise notation to represent some more complex kinds of types that have not yet been discussed, such as inference variables. - -[ch_representing_types]: ../ty.md - -Understanding this page likely requires a rudimentary understanding of higher ranked -trait bounds/`for<'a>`and also what types such as `dyn for<'a> Trait<'a>` and - `for<'a> fn(&'a u32)` mean. Reading [the nomincon chapter](https://doc.rust-lang.org/nomicon/hrtb.html) -on HRTB may be useful for understanding this syntax. The meaning of `for<'a> fn(&'a u32)` -is incredibly similar to the meaning of `T: for<'a> Trait<'a>`. - -## What does it mean for parameters to be early or late bound - -All function definitions conceptually have a ZST (this is represented by `TyKind::FnDef` in rustc). -The only generics on this ZST are the early bound parameters of the function definition. e.g. -```rust -fn foo<'a>(_: &'a u32) {} - -fn main() { - let b = foo; - // ^ `b` has type `FnDef(foo, [])` (no args because `'a` is late bound) - assert!(std::mem::size_of_val(&b) == 0); -} -``` - -In order to call `b` the late bound parameters do need to be provided, these are inferred at the -call site instead of when we refer to `foo`. -```rust -fn main() { - let b = foo; - let a: &'static u32 = &10; - foo(a); - // the lifetime argument for `'a` on `foo` is inferred at the callsite - // the generic parameter `'a` on `foo` is inferred to `'static` here -} -``` - -Because late bound parameters are not part of the `FnDef`'s args this allows us to prove trait -bounds such as `F: for<'a> Fn(&'a u32)` where `F` is `foo`'s `FnDef`. e.g. -```rust -fn foo_early<'a, T: Trait<'a>>(_: &'a u32, _: T) {} -fn foo_late<'a, T>(_: &'a u32, _: T) {} - -fn accepts_hr_func Fn(&'a u32, u32)>(_: F) {} - -fn main() { - // doesn't work, the instantiated bound is `for<'a> FnDef<'?0>: Fn(&'a u32, u32)` - // `foo_early` only implements `for<'a> FnDef<'a>: Fn(&'a u32, u32)`- the lifetime - // of the borrow in the function argument must be the same as the lifetime - // on the `FnDef`. - accepts_hr_func(foo_early); - - // works, the instantiated bound is `for<'a> FnDef: Fn(&'a u32, u32)` - accepts_hr_func(foo_late); -} - -// the builtin `Fn` impls for `foo_early` and `foo_late` look something like: -// `foo_early` -impl<'a, T: Trait<'a>> Fn(&'a u32, T) for FooEarlyFnDef<'a, T> { ... } -// `foo_late` -impl<'a, T> Fn(&'a u32, T) for FooLateFnDef { ... } - -``` - -Early bound parameters are present on the `FnDef`. Late bound generic parameters are not present -on the `FnDef` but are instead constrained by the builtin `Fn*` impl. - -The same distinction applies to closures. Instead of `FnDef` we are talking about the anonymous -closure type. Closures are [currently unsound](https://github.com/rust-lang/rust/issues/84366) in -ways that are closely related to the distinction between early/late bound -parameters (more on this later) - -The early/late boundness of generic parameters is only relevant for the desugaring of -functions/closures into types with builtin `Fn*` impls. It does not make sense to talk about -in other contexts. - -The `generics_of` query in rustc only contains early bound parameters. In this way it acts more -like `generics_of(my_func)` is the generics for the FnDef than the generics provided to the function -body although it's not clear to the author of this section if this was the actual justification for -making `generics_of` behave this way. - -## What parameters are currently late bound - -Below are the current requirements for determining if a generic parameter is late bound. It is worth -keeping in mind that these are not necessarily set in stone and it is almost certainly possible to -be more flexible. - -### Must be a lifetime parameter - -Rust can't support types such as `for dyn Trait` or `for fn(T)`, this is a -fundamental limitation of the language as we are required to monomorphize type/const -parameters and cannot do so behind dynamic dispatch. (technically we could probably -support `for dyn MarkerTrait` as there is nothing to monomorphize) - -Not being able to support `for dyn Trait` resulted in making all type and const -parameters early bound. Only lifetime parameters can be late bound. - -### Must not appear in the where clauses - -In order for a generic parameter to be late bound it must not appear in any where clauses. -This is currently an incredibly simplistic check that causes lifetimes to be early bound even -if the where clause they appear in are always true, or implied by well formedness of function -arguments. e.g. -```rust -fn foo1<'a: 'a>(_: &'a u32) {} -// ^^ early bound parameter because it's in a `'a: 'a` clause -// even though the bound obviously holds all the time -fn foo2<'a, T: Trait<'a>(a: T, b: &'a u32) {} -// ^^ early bound parameter because it's used in the `T: Trait<'a>` clause -fn foo3<'a, T: 'a>(_: &'a T) {} -// ^^ early bound parameter because it's used in the `T: 'a` clause -// even though that bound is implied by wellformedness of `&'a T` -fn foo4<'a, 'b: 'a>(_: Inv<&'a ()>, _: Inv<&'b ()>) {} -// ^^ ^^ ^^^ note: -// ^^ ^^ `Inv` stands for `Invariant` and is used to -// ^^ ^^ make the type parameter invariant. This -// ^^ ^^ is necessary for demonstration purposes as -// ^^ ^^ `for<'a, 'b> fn(&'a (), &'b ())` and -// ^^ ^^ `for<'a> fn(&'a u32, &'a u32)` are subtypes- -// ^^ ^^ of each other which makes the bound trivially -// ^^ ^^ satisfiable when making the fnptr. `Inv` -// ^^ ^^ disables this subtyping. -// ^^ ^^ -// ^^^^^^ both early bound parameters because they are present in the -// `'b: 'a` clause -``` - -The reason for this requirement is that we cannot represent the `T: Trait<'a>` or `'a: 'b` clauses -on a function pointer. `for<'a, 'b> fn(Inv<&'a ()>, Inv<&'b ()>)` is not a valid function pointer to -represent`foo4` as it would allow calling the function without `'b: 'a` holding. - -### Must be constrained by where clauses or function argument types - -The builtin impls of the `Fn*` traits for closures and `FnDef`s cannot not have any unconstrained -parameters. For example the following impl is illegal: -```rust -impl<'a> Trait for u32 { type Assoc = &'a u32; } -``` -We must not end up with a similar impl for the `Fn*` traits e.g. -```rust -impl<'a> Fn<()> for FnDef { type Assoc = &'a u32 } -``` - -Violating this rule can trivially lead to unsoundness as seen in [#84366](https://github.com/rust-lang/rust/issues/84366). -Additionally if we ever support late bound type params then an impl like: -```rust -impl Fn<()> for FnDef { type Assoc = T; } -``` -would break the compiler in various ways. - -In order to ensure that everything functions correctly, we do not allow generic parameters to -be late bound if it would result in a builtin impl that does not constrain all of the generic -parameters on the builtin impl. Making a generic parameter be early bound trivially makes it be -constrained by the builtin impl as it ends up on the self type. - -Because of the requirement that late bound parameters must not appear in where clauses, checking -this is simpler than the rules for checking impl headers constrain all the parameters on the impl. -We only have to ensure that all late bound parameters appear at least once in the function argument -types outside of an alias (e.g. an associated type). - -The requirement that they not indirectly be in the args of an alias for it to count is the -same as why the follow code is forbidden: -```rust -impl OtherTrait for ::Assoc { type Assoc = T } -``` -There is no guarantee that `::Assoc` will normalize to different types for every -instantiation of `T`. If we were to allow this impl we could get overlapping impls and the -same is true of the builtin `Fn*` impls. - -## Making more generic parameters late bound - -It is generally considered desirable for more parameters to be late bound as it makes -the builtin `Fn*` impls more flexible. Right now many of the requirements for making -a parameter late bound are overly restrictive as they are tied to what we can currently -(or can ever) do with fn ptrs. - -It would be theoretically possible to support late bound params in `where`-clauses in the -language by introducing implication types which would allow us to express types such as: -`for<'a, 'b: 'a> fn(Inv<&'a u32>, Inv<&'b u32>)` which would ensure `'b: 'a` is upheld when -calling the function pointer. - -It would also be theoretically possible to support it by making the coercion to a fn ptr -instantiate the parameter with an infer var while still allowing the FnDef to not have the -generic parameter present as trait impls are perfectly capable of representing the where clauses -on the function on the impl itself. This would also allow us to support late bound type/const -vars allowing bounds like `F: for Fn(T)` to hold. - -It is almost somewhat unclear if we can change the `Fn` traits to be structured differently -so that we never have to make a parameter early bound just to make the builtin impl have all -generics be constrained. Of all the possible causes of a generic parameter being early bound -this seems the most difficult to remove. - -Whether these would be good ideas to implement is a separate question- they are only brought -up to illustrate that the current rules are not necessarily set in stone and a result of -"its the only way of doing this". - diff --git a/src/doc/rustc-dev-guide/src/early-late-bound-params/turbofishing-and-early-late-bound.md b/src/doc/rustc-dev-guide/src/early-late-bound-params/turbofishing-and-early-late-bound.md deleted file mode 100644 index 6fc30c6767d2..000000000000 --- a/src/doc/rustc-dev-guide/src/early-late-bound-params/turbofishing-and-early-late-bound.md +++ /dev/null @@ -1,125 +0,0 @@ -# Turbofishing's interactions with early/late bound parameters - -> Note: this chapter makes reference to information discussed later on in the [representing types][ch_representing_types] chapter. Specifically, it uses concise notation to represent some more complex kinds of types that have not yet been discussed, such as inference variables. - -[ch_representing_types]: ../ty.md - -The early/late bound parameter distinction on functions introduces some complications -when providing generic arguments to functions. This document discusses what those are -and how they might interact with future changes to make more things late bound. - -## Can't turbofish generic arguments on functions sometimes - -When a function has any late bound lifetime parameters (be they explicitly defined or -implicitly introduced via lifetime elision) we disallow specifying any lifetime arguments -on the function. Sometimes this is a hard error other times it is a future compat lint -([`late_bound_lifetime_arguments`](https://github.com/rust-lang/rust/issues/42868)). - -```rust -fn early<'a: 'a>(a: &'a ()) -> &'a () { a } -fn late<'a>(a: &'a ()) -> &'a () { a } - -fn mixed<'a, 'b: 'b>(a: &'a (), b: &'b ()) -> &'a () { a } - -struct Foo; -impl Foo { - fn late<'a>(self, a: &'a ()) -> &'a () { a } -} - -fn main() { - // fine - let f = early::<'static>; - - // some variation of hard errors and future compat lints - Foo.late::<'static>(&()); - let f = late::<'static>; - let f = mixed::<'static, 'static>; - let f = mixed::<'static>; - late::<'static>(&()); -} -``` - -The justification for this is that late bound parameters are not present on the -`FnDef` so the arguments to late bound parameters can't be present in the generic arguments -for the type. i.e. the `late` function in the above code snippet would not have -any generic parameters on the `FnDef` zst: -```rust -// example desugaring of the `late` function and its zst + builtin Fn impl -struct LateFnDef; -impl<'a> Fn<(&'a ())> for LateFnDef { - type Output = &'a (); - ... -} -``` - -The cause for some situations giving future compat lints and others giving hard errors -is a little arbitrary but explainable: -- It's always a hard error for method calls -- It's only a hard error on paths to free functions if there is no unambiguous way to -create the generic arguments for the fndef from the lifetime arguments. (i.e. the amount of -lifetimes provided must be exactly equal to the amount of early bound lifetimes or -else it's a hard error) - -## Back compat issues from turning early bound to late bound - -Because of the previously mentioned restriction on turbofishing generic arguments, it -is a breaking change to upgrade a lifetime from early bound to late bound as it can cause -existing turbofishies to become hard errors/future compat lints. - -Many t-types members have expressed interest in wanting more parameters to be late bound. -We cannot do so if making something late bound is going to break code that many would -expect to work (judging by the future compat lint issue many people do expect to be able -to turbofish late bound parameters). - -## Interactions with late bound type/const parameters - -If we were to make some type/const parameters late bound we would definitely not want -to disallow turbofishing them as it presumably(?) would break a Tonne of code. - -While lifetimes do differ from type/consts in some ways I(BoxyUwU) do not believe there -is any justification for why it would make sense to allow turbofishing late bound -type/const parameters but not late bound lifetimes. - -## Removing the hard error/fcw - -From reasons above it seems reasonable that we may want to remove the hard error and fcw -(removing the errors/fcw is definitely a blocker for making more things late bound). - -example behaviour: -```rust -fn late<'a>(a: &'a ()) -> &'a () { a } - -fn accepts_fn(_: impl for<'a> Fn(&'a ()) -> &'a ()) {} -fn accepts_fn_2(_: impl Fn(&'static ()) -> &'static ()) {} - -fn main() { - let f = late::<'static>; - - accepts_fn(f); //~ error: `f` doesn't implement `for<'a> Fn(&'a ()) -> &'a ()` - accepts_fn_2(f) // works - - accepts_fn(late) // works -} -```` - -one potential complication is that we would want a way to specify a generic argument -to a function without having to specify arguments for all previous parameters. i.e. -ideally you could write the following code somehow. -```rust -fn late<'a, 'b>(_: &'a (), _: &'b ()) {} - -fn accepts_fn(_: impl for<'a> Fn(&'a (), &'static ())) {} - -fn main() { - // a naive implementation would have an inference variable as - // the argument to the `'a` parameter no longer allowing the `FnDef` - // to satisfy the bound `for<'a> Fn(&'a ())` - let f = late::<'_, 'static>; - accepts_fn(f); -} -``` -Maybe we can just special case HIR ty lowering for `_`/`'_` arguments for late bound -parameters somehow and have it not mean the same thing as `_` for early bound parameters. -Regardless I think we would need a solution that would allow writing the above code even -if it was done by some new syntax such as having to write `late::` -(naturally `k#no_argument` would only make sense as an argument to late bound parameters). diff --git a/src/doc/rustc-dev-guide/src/early_late_parameters.md b/src/doc/rustc-dev-guide/src/early_late_parameters.md new file mode 100644 index 000000000000..6d13655294d3 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/early_late_parameters.md @@ -0,0 +1,424 @@ + +# Early vs Late bound parameters + + + +> **NOTE**: This chapter largely talks about early/late bound as being solely relevant when discussing function item types/function definitions. This is potentially not completely true, async blocks and closures should likely be discussed somewhat in this chapter. + +## What does it mean to be "early" bound or "late" bound + +Every function definition has a corresponding ZST that implements the `Fn*` traits known as a [function item type][function_item_type]. This part of the chapter will talk a little bit about the "desugaring" of function item types as it is useful context for explaining the difference between early bound and late bound generic parameters. + +Let's start with a very trivial example involving no generic parameters: + +```rust +fn foo(a: String) -> u8 { + # 1 + /* snip */ +} +``` + +If we explicitly wrote out the definitions for the function item type corresponding to `foo` and its associated `Fn` impl it would look something like this: +```rust,ignore +struct FooFnItem; + +impl Fn<(String,)> for FooFnItem { + type Output = u8; + /* fn call(&self, ...) -> ... { ... } */ +} +``` + +The builtin impls for the `FnMut`/`FnOnce` traits as well as the impls for `Copy` and `Clone` were omitted for brevity reasons (although these traits *are* implemented for function item types). + +A slightly more complicated example would involve introducing generic parameters to the function: +```rust +fn foo(a: T) -> T { + # a + /* snip */ +} +``` +Writing out the definitions would look something like this: +```rust,ignore +struct FooFnItem(PhantomData T>); + +impl Fn<(T,)> for FooFnItem { + type Output = T; + /* fn call(&self, ...) -> ... { ... } */ +} +``` + +Note that the function item type `FooFnItem` is generic over some type parameter `T` as defined on the function `foo`. However, not all generic parameters defined on functions are also defined on the function item type as demonstrated here: +```rust +fn foo<'a, T: Sized>(a: &'a T) -> &'a T { + # a + /* snip */ +} +``` +With its "desugared" form looking like so: +```rust,ignore +struct FooFnItem(PhantomData fn(&'a T) -> &'a T>); + +impl<'a, T: Sized> Fn<(&'a T,)> for FooFnItem { + type Output = &'a T; + /* fn call(&self, ...) -> ... { ... } */ +} +``` + +The lifetime parameter `'a` from the function `foo` is not present on the function item type `FooFnItem` and is instead introduced on the builtin impl solely for use in representing the argument types. + +Generic parameters not all being defined on the function item type means that there are two steps where generic arguments are provided when calling a function. +1. Naming the function (e.g. `let a = foo;`) the arguments for `FooFnItem` are provided. +2. Calling the function (e.g. `a(&10);`) any parameters defined on the builtin impl are provided. + +This two-step system is where the early vs late naming scheme comes from, early bound parameters are provided in the *earliest* step (naming the function), whereas late bound parameters are provided in the *latest* step (calling the function). + +Looking at the desugaring from the previous example we can tell that `T` is an early bound type parameter and `'a` is a late bound lifetime parameter as `T` is present on the function item type but `'a` is not. See this example of calling `foo` annotated with where each generic parameter has an argument provided: +```rust +fn foo<'a, T: Sized>(a: &'a T) -> &'a T { + # a + /* snip */ +} + +// Here we provide a type argument `String` to the +// type parameter `T` on the function item type +let my_func = foo::; + +// Here (implicitly) a lifetime argument is provided +// to the lifetime parameter `'a` on the builtin impl. +my_func(&String::new()); +``` + +[function_item_type]: https://doc.rust-lang.org/reference/types/function-item.html + +## Differences between early and late bound parameters + +### Higher ranked function pointers and trait bounds + +A generic parameter being late bound allows for more flexible usage of the function item. For example if we have some function `foo` with an early bound lifetime parameter and some function `bar` with a late bound lifetime parameter `'a` we would have the following builtin `Fn` impls: +```rust,ignore +impl<'a> Fn<(&'a String,)> for FooFnItem<'a> { /* ... */ } +impl<'a> Fn<(&'a String,)> for BarFnItem { /* ... */ } +``` + +The `bar` function has a strictly more flexible signature as the function item type can be called with a borrow with *any* lifetime, whereas the `foo` function item type would only be callable with a borrow with the same lifetime on the function item type. We can show this by simply trying to call `foo`'s function item type multiple times with different lifetimes: + +```rust +// The `'a: 'a` bound forces this lifetime to be early bound. +fn foo<'a: 'a>(b: &'a String) -> &'a String { b } +fn bar<'a>(b: &'a String) -> &'a String { b } + +// Early bound generic parameters are instantiated here when naming +// the function `foo`. As `'a` is early bound an argument is provided. +let f = foo::<'_>; + +// Both function arguments are required to have the same lifetime as +// the lifetime parameter being early bound means that `f` is only +// callable for one specific lifetime. +// +// As we call this with borrows of different lifetimes, the borrow checker +// will error here. +f(&String::new()); +f(&String::new()); +``` + +In this example we call `foo`'s function item type twice, each time with a borrow of a temporary. These two borrows could not possible have lifetimes that overlap as the temporaries are only alive during the function call, not after. The lifetime parameter on `foo` being early bound requires all callers of `f` to provide a borrow with the same lifetime, as this is not possible the borrow checker errors. + +If the lifetime parameter on `foo` was late bound this would be able to compile as each caller could provide a different lifetime argument for its borrow. See the following example which demonstrates this using the `bar` function defined above: + +```rust +#fn foo<'a: 'a>(b: &'a String) -> &'a String { b } +#fn bar<'a>(b: &'a String) -> &'a String { b } + +// Early bound parameters are instantiated here, however as `'a` is +// late bound it is not provided here. +let b = bar; + +// Late bound parameters are instantiated separately at each call site +// allowing different lifetimes to be used by each caller. +b(&String::new()); +b(&String::new()); +``` + +This is reflected in the ability to coerce function item types to higher ranked function pointers and prove higher ranked `Fn` trait bounds. We can demonstrate this with the following example: +```rust +// The `'a: 'a` bound forces this lifetime to be early bound. +fn foo<'a: 'a>(b: &'a String) -> &'a String { b } +fn bar<'a>(b: &'a String) -> &'a String { b } + +fn accepts_hr_fn(_: impl for<'a> Fn(&'a String) -> &'a String) {} + +fn higher_ranked_trait_bound() { + let bar_fn_item = bar; + accepts_hr_fn(bar_fn_item); + + let foo_fn_item = foo::<'_>; + // errors + accepts_hr_fn(foo_fn_item); +} + +fn higher_ranked_fn_ptr() { + let bar_fn_item = bar; + let fn_ptr: for<'a> fn(&'a String) -> &'a String = bar_fn_item; + + let foo_fn_item = foo::<'_>; + // errors + let fn_ptr: for<'a> fn(&'a String) -> &'a String = foo_fn_item; +} +``` + +In both of these cases the borrow checker errors as it does not consider `foo_fn_item` to be callable with a borrow of any lifetime. This is due to the fact that the lifetime parameter on `foo` is early bound, causing `foo_fn_item` to have a type of `FooFnItem<'_>` which (as demonstrated by the desugared `Fn` impl) is only callable with a borrow of the same lifetime `'_`. + +### Turbofishing in the presence of late bound parameters + +As mentioned previously, the distinction between early and late bound parameters means that there are two places where generic parameters are instantiated: +- When naming a function (early) +- When calling a function (late) + +There currently is no syntax for explicitly specifying generic arguments for late bound parameters as part of the call step, only specifying generic arguments when naming a function. The syntax `foo::<'static>();`, despite being part of a function call, behaves as `(foo::<'static>)();` and instantiates the early bound generic parameters on the function item type. + +See the following example: +```rust +fn foo<'a>(b: &'a u32) -> &'a u32 { b } + +let f /* : FooFnItem */ = foo::<'static>; +``` + +The above example errors as the lifetime parameter `'a` is late bound and so cannot be instantiated as part of the "naming a function" step. If we make the lifetime parameter early bound we will see this code start to compile: +```rust +fn foo<'a: 'a>(b: &'a u32) -> &'a u32 { b } + +let f /* : FooFnItem<'static> */ = foo::<'static>; +``` + +What the current implementation of the compiler aims to do is error when specifying lifetime arguments to a function that has both early *and* late bound lifetime parameters. In practice, due to excessive breakage, some cases are actually only future compatibility warnings ([#42868](https://github.com/rust-lang/rust/issues/42868)): +- When the amount of lifetime arguments is the same as the number of early bound lifetime parameters a FCW is emitted instead of an error +- An error is always downgraded to a FCW when using method call syntax + +To demonstrate this we can write out the different kinds of functions and give them both a late and early bound lifetime: +```rust,ignore +fn free_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} + +struct Foo; + +trait Trait: Sized { + fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()); + fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()); +} + +impl Trait for Foo { + fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} + fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +} + +impl Foo { + fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} + fn inherent_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +} +``` + +Then, for the first case, we can call each function with a single lifetime argument (corresponding to the one early bound lifetime parameter) and note that it only results in a FCW rather than a hard error. +```rust +#![deny(late_bound_lifetime_arguments)] + +#fn free_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +# +#struct Foo; +# +#trait Trait: Sized { +# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()); +# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()); +#} +# +#impl Trait for Foo { +# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} +# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +#} +# +#impl Foo { +# fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} +# fn inherent_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +#} +# +// Specifying as many arguments as there are early +// bound parameters is always a future compat warning +Foo.trait_method::<'static>(&(), &()); +Foo::trait_method::<'static>(Foo, &(), &()); +Foo::trait_function::<'static>(&(), &()); +Foo.inherent_method::<'static>(&(), &()); +Foo::inherent_function::<'static>(&(), &()); +free_function::<'static>(&(), &()); +``` + +For the second case we call each function with more lifetime arguments than there are lifetime parameters (be it early or late bound) and note that method calls result in a FCW as opposed to the free/associated functions which result in a hard error: +```rust +#fn free_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +# +#struct Foo; +# +#trait Trait: Sized { +# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()); +# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()); +#} +# +#impl Trait for Foo { +# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} +# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +#} +# +#impl Foo { +# fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} +# fn inherent_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +#} +# +// Specifying more arguments than there are early +// bound parameters is a future compat warning when +// using method call syntax. +Foo.trait_method::<'static, 'static, 'static>(&(), &()); +Foo.inherent_method::<'static, 'static, 'static>(&(), &()); +// However, it is a hard error when not using method call syntax. +Foo::trait_method::<'static, 'static, 'static>(Foo, &(), &()); +Foo::trait_function::<'static, 'static, 'static>(&(), &()); +Foo::inherent_function::<'static, 'static, 'static>(&(), &()); +free_function::<'static, 'static, 'static>(&(), &()); +``` + +Even when specifying enough lifetime arguments for both the late and early bound lifetime parameter, these arguments are not actually used to annotate the lifetime provided to late bound parameters. We can demonstrate this by turbofishing `'static` to a function while providing a non-static borrow: +```rust +struct Foo; + +impl Foo { + fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b String ) {} +} + +Foo.inherent_method::<'static, 'static>(&(), &String::new()); +``` + +This compiles even though the `&String::new()` function argument does not have a `'static` lifetime, this is because "extra" lifetime arguments are discarded rather than taken into account for late bound parameters when actually calling the function. + +### Liveness of types with late bound parameters + +When checking type outlives bounds involving function item types we take into account early bound parameters. For example: + +```rust +fn foo(_: T) {} + +fn requires_static(_: T) {} + +fn bar() { + let f /* : FooFnItem */ = foo::; + requires_static(f); +} +``` + +As the type parameter `T` is early bound, the desugaring of the function item type for `foo` would look something like `struct FooFnItem`. Then in order for `FooFnItem: 'static` to hold we must also require `T: 'static` to hold as otherwise we would wind up with soundness bugs. + +Unfortunately, due to bugs in the compiler, we do not take into account early bound *lifetimes*, which is the cause of the open soundness bug [#84366](https://github.com/rust-lang/rust/issues/84366). This means that it's impossible to demonstrate a "difference" between early/late bound parameters for liveness/type outlives bounds as the only kind of generic parameters that are able to be late bound are lifetimes which are handled incorrectly. + +Regardless, in theory the code example below *should* demonstrate such a difference once [#84366](https://github.com/rust-lang/rust/issues/84366) is fixed: +```rust +fn early_bound<'a: 'a>(_: &'a String) {} +fn late_bound<'a>(_: &'a String) {} + +fn requires_static(_: T) {} + +fn bar<'b>() { + let e = early_bound::<'b>; + // this *should* error but does not + requires_static(e); + + let l = late_bound; + // this correctly does not error + requires_static(l); +} +``` + +## Requirements for a parameter to be late bound + +### Must be a lifetime parameter + +Type and Const parameters are not able to be late bound as we do not have a way to support types such as `dyn for Fn(Box)` or `for fn(Box)`. Calling such types requires being able to monomorphize the underlying function which is not possible with indirection through dynamic dispatch. + +### Must not be used in a where clause + +Currently when a generic parameter is used in a where clause it must be early bound. For example: +```rust +# trait Trait<'a> {} +fn foo<'a, T: Trait<'a>>(_: &'a String, _: T) {} +``` + +In this example the lifetime parameter `'a` is considered to be early bound as it appears in the where clause `T: Trait<'a>`. This is true even for "trivial" where clauses such as `'a: 'a` or those implied by wellformedness of function arguments, for example: +```rust +fn foo<'a: 'a>(_: &'a String) {} +fn bar<'a, T: 'a>(_: &'a T) {} +``` + +In both of these functions the lifetime parameter `'a` would be considered to be early bound even though the where clauses they are used in arguably do not actually impose any constraints on the caller. + +The reason for this restriction is a combination of two things: +- We cannot prove bounds on late bound parameters until they have been instantiated +- Function pointers and trait objects do not have a way to represent yet to be proven where clauses from the underlying function + +Take the following example: +```rust +trait Trait<'a> {} +fn foo<'a, T: Trait<'a>>(_: &'a T) {} + +let f = foo::; +let f = f as for<'a> fn(&'a String); +f(&String::new()); +``` + +At *some point* during type checking an error should be emitted for this code as `String` does not implement `Trait` for any lifetime. + +If the lifetime `'a` were late bound then this becomes difficult to check. When naming `foo` we do not know what lifetime should be used as part of the `T: Trait<'a>` trait bound as it has not yet been instantiated. When coercing the function item type to a function pointer we have no way of tracking the `String: Trait<'a>` trait bound that must be proven when calling the function. + +If the lifetime `'a` is early bound (which it is in the current implementation in rustc), then the trait bound can be checked when naming the function `foo`. Requiring parameters used in where clauses to be early bound gives a natural place to check where clauses defined on the function. + +Finally, we do not require lifetimes to be early bound if they are used in *implied bounds*, for example: +```rust +fn foo<'a, T>(_: &'a T) {} + +let f = foo; +f(&String::new()); +f(&String::new()); +``` + +This code compiles, demonstrating that the lifetime parameter is late bound, even though `'a` is used in the type `&'a T` which implicitly requires `T: 'a` to hold. Implied bounds can be treated specially as any types introducing implied bounds are in the signature of the function pointer type, which means that when calling the function we know to prove `T: 'a`. + +### Must be constrained by argument types + +It is important that builtin impls on function item types do not wind up with unconstrained generic parameters as this can lead to unsoundness. This is the same kind of restriction as applies to user written impls, for example the following code results in an error: +```rust +trait Trait { + type Assoc; +} + +impl<'a> Trait for u8 { + type Assoc = &'a String; +} +``` + +The analogous example for builtin impls on function items would be the following: +```rust,ignore +fn foo<'a>() -> &'a String { /* ... */ } +``` +If the lifetime parameter `'a` were to be late bound we would wind up with a builtin impl with an unconstrained lifetime, we can manually write out the desugaring for the function item type and its impls with `'a` being late bound to demonstrate this: +```rust,ignore +// NOTE: this is just for demonstration, in practice `'a` is early bound +struct FooFnItem; + +impl<'a> Fn<()> for FooFnItem { + type Output = &'a String; + /* fn call(...) -> ... { ... } */ +} +``` + +In order to avoid such a situation we consider `'a` to be early bound which causes the lifetime on the impl to be constrained by the self type: +```rust,ignore +struct FooFnItem<'a>(PhantomData &'a String>); + +impl<'a> Fn<()> for FooFnItem<'a> { + type Output = &'a String; + /* fn call(...) -> ... { ... } */ +} +``` \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index 533f7eb5e734..a7ab3d773acb 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -3,24 +3,24 @@ The `rust-lang/rust` git repository depends on several other repos in the `rust-lang` organization. There are three main ways we use dependencies: 1. As a Cargo dependency through crates.io (e.g. `rustc-rayon`) -2. As a git subtree (e.g. `clippy`) +2. As a git (e.g. `clippy`) or a [josh] (e.g. `miri`) subtree 3. As a git submodule (e.g. `cargo`) -As a general rule, use crates.io for libraries that could be useful for others in the ecosystem; use -subtrees for tools that depend on compiler internals and need to be updated if there are breaking -changes; and use submodules for tools that are independent of the compiler. +As a general rule: +- Use crates.io for libraries that could be useful for others in the ecosystem +- Use subtrees for tools that depend on compiler internals and need to be updated if there are breaking +changes +- Use submodules for tools that are independent of the compiler -## External Dependencies (subtree) +## External Dependencies (subtrees) -As a developer to this repository, you don't have to treat the following external projects -differently from other crates that are directly in this repo: +The following external projects are managed using some form of a `subtree`: -* [Clippy](https://github.com/rust-lang/rust-clippy) -* [Miri] +* [clippy](https://github.com/rust-lang/rust-clippy) +* [miri](https://github.com/rust-lang/miri) * [rustfmt](https://github.com/rust-lang/rustfmt) * [rust-analyzer](https://github.com/rust-lang/rust-analyzer) - -[Miri]: https://github.com/rust-lang/miri +* [rustc_codegen_cranelift](https://github.com/rust-lang/rustc_codegen_cranelift) In contrast to `submodule` dependencies (see below for those), the `subtree` dependencies are just regular files and directories which can @@ -29,6 +29,20 @@ to these tools should be filed against the tools directly in their respective upstream repositories. The exception is that when rustc changes are required to implement a new tool feature or test, that should happen in one collective rustc PR. +`subtree` dependencies are currently managed by two distinct approaches: + +* Using `git subtree` + * `clippy` ([sync guide](https://doc.rust-lang.org/nightly/clippy/development/infrastructure/sync.html#performing-the-sync-from-rust-langrust-to-clippy)) + * `rustfmt` + * `rustc_codegen_cranelift` ([sync script](https://github.com/rust-lang/rustc_codegen_cranelift/blob/113af154d459e41b3dc2c5d7d878e3d3a8f33c69/scripts/rustup.sh#L7)) +* Using the [josh] tool + * `miri` ([sync guide](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#advanced-topic-syncing-with-the-rustc-repo)) + * `rust-analyzer` ([sync script](https://github.com/rust-lang/rust-analyzer/blob/2e13684be123eca7181aa48e043e185d8044a84a/xtask/src/release.rs#L147)) + +The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh, you can check out the `miri` or `rust-analyzer` scripts linked above for inspiration. If you want to migrate a subtree from `git subtree` to josh, you can check out [this guide](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg). + +Below you can find a guide on how to perform push and pull synchronization with the main rustc repo using `git subtree`, although these instructions might differ repo from repo. + ### Synchronizing a subtree Periodically the changes made to subtree based dependencies need to be synchronized between this @@ -84,7 +98,6 @@ Now you're done, the `src/tools/clippy` directory behaves as if Clippy were part of the rustc monorepo, so no one but you (or others that synchronize subtrees) actually needs to use `git subtree`. - ## External Dependencies (submodules) Building Rust will also use external git repositories tracked using [git @@ -111,3 +124,4 @@ the week leading up to the beta cut. [The Rust Reference]: https://github.com/rust-lang/reference/ [toolstate website]: https://rust-lang-nursery.github.io/rust-toolstate/ [Toolstate chapter]: https://forge.rust-lang.org/infra/toolstate.html +[josh]: https://josh-project.github.io/josh/intro.html diff --git a/src/doc/rustc-dev-guide/src/generic_parameters_summary.md b/src/doc/rustc-dev-guide/src/generic_parameters_summary.md index 3403c9f2991a..da38ba0455c2 100644 --- a/src/doc/rustc-dev-guide/src/generic_parameters_summary.md +++ b/src/doc/rustc-dev-guide/src/generic_parameters_summary.md @@ -25,42 +25,4 @@ Interestingly, `ty::Generics` does not currently contain _every_ generic paramet [ch_representing_types]: ./ty.md [`ty::Generics`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Generics.html [`GenericParamDef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/generics/struct.GenericParamDef.html - -# Early vs Late bound parameters - - -```rust -fn foo<'a, T>(b: &'a T) -> &'a T { b } -// ^^ ^early bound -// ^^ -// ^^late bound -``` - -Generally when referring to an item with generic parameters you must specify a list of generic arguments corresponding to the item's generic parameters. In some cases it is permitted to elide these arguments but still, implicitly, a set of arguments are provided (e.g. `Vec::default()` desugars to `Vec::<_>::default()`). - -For functions this is not necessarily the case, for example if we take the function `foo` from the example above and write the following code: -```rust -fn main() { - let f = foo::<_>; - - let b = String::new(); - let c = String::new(); - - f(&b); - drop(b); - f(&c); -} -``` - -This code compiles perfectly fine even though there is no single lifetime that could possibly be specified in `foo::<_>` that would allow for both -the `&b` and `&c` borrows to be used as arguments (note: the `drop(b)` line forces the `&b` borrow to be shorter than the `&c` borrow). This works because the `'a` lifetime is _late bound_. - -A generic parameter being late bound means that when we write `foo::<_>` we do not actually provide an argument for that parameter, instead we wait until _calling_ the function to provide the generic argument. In the above example this means that we are doing something like `f::<'_>(&b);` and `f::<'_>(&c);` (although in practice we do not actually support turbofishing late bound parameters in this manner) - -It may be helpful to think of "early bound parameter" or "late bound parameter" as meaning "early provided parameter" and "late provided parameter", i.e. we provide the argument to the parameter either early (when naming the function) or late (when calling it). - -Late bound parameters on functions are tracked with a [`Binder`] when accessing the signature of the function, this can be done with the [`fn_sig`] query. For more information of binders see the [chapter on `Binder`s ][ch_binders]. - -[`Binder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/binder/struct.Binder.html -[`fn_sig`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.fn_sig -[ch_binders]: ./ty_module/binders.md \ No newline at end of file +[ch_binders]: ./ty_module/binders.md diff --git a/src/doc/rustc-dev-guide/src/method-lookup.md b/src/doc/rustc-dev-guide/src/method-lookup.md index 8b49e8d004b1..c8d529a32b53 100644 --- a/src/doc/rustc-dev-guide/src/method-lookup.md +++ b/src/doc/rustc-dev-guide/src/method-lookup.md @@ -67,6 +67,7 @@ imported to use an inherent method, they are associated with the type itself (note that inherent impls can only be defined in the same crate as the type itself). + **Extension candidates** are derived from imported traits. If I have the trait `ToString` imported, and I call `to_string()` as a method, diff --git a/src/doc/rustc-dev-guide/src/solve/normalization.md b/src/doc/rustc-dev-guide/src/solve/normalization.md index 8858258b5426..99dc20c46b5d 100644 --- a/src/doc/rustc-dev-guide/src/solve/normalization.md +++ b/src/doc/rustc-dev-guide/src/solve/normalization.md @@ -1,5 +1,7 @@ # Normalization in the new solver +> FIXME: Normalization has been changed significantly since this chapter was written. + With the new solver we've made some fairly significant changes to normalization when compared to the existing implementation. @@ -52,12 +54,14 @@ before assigning the resulting rigid type to an inference variable. This is used This has to be used whenever we match on the value of some type, both inside and outside of the trait solver. + [structural_norm]: https://github.com/rust-lang/rust/blob/2627e9f3012a97d3136b3e11bf6bd0853c38a534/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L140-L175 [structural-relate]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L88-L107 @@ -72,11 +76,13 @@ possible. However, this only works for aliases referencing bound variables if th not ambiguous as we're unable to replace the alias with a corresponding inference variable without leaking universes. + [generalize-no-alias]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_infer/src/infer/relate/generalize.rs#L353-L358 diff --git a/src/doc/rustc-dev-guide/src/solve/opaque-types.md b/src/doc/rustc-dev-guide/src/solve/opaque-types.md index 87531705c2da..672aab770801 100644 --- a/src/doc/rustc-dev-guide/src/solve/opaque-types.md +++ b/src/doc/rustc-dev-guide/src/solve/opaque-types.md @@ -60,6 +60,7 @@ Finally, we check whether the item bounds of the opaque hold for the expected ty [eq-prev]: https://github.com/rust-lang/rust/blob/384d26fc7e3bdd7687cc17b2662b091f6017ec2a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs#L51-L59 [insert-storage]: https://github.com/rust-lang/rust/blob/384d26fc7e3bdd7687cc17b2662b091f6017ec2a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs#L68 [item-bounds-ck]: https://github.com/rust-lang/rust/blob/384d26fc7e3bdd7687cc17b2662b091f6017ec2a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs#L69-L74 + [^1]: FIXME: this should ideally only result in a unique candidate given that we require the args to be placeholders and regions are always inference vars [^2]: FIXME: why do we check whether the expected type is rigid for this. @@ -101,6 +102,7 @@ The handling of member constraints does not change in the new solver. See the FIXME: We need to continue to support calling methods on still unconstrained opaque types in their defining scope. It's unclear how to best do this. + ```rust use std::future::Future; use futures::FutureExt; diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index 5e27a2fd770c..0ad1d2a28ede 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -28,7 +28,7 @@ Our CI is primarily executed on [GitHub Actions], with a single workflow defined in [`.github/workflows/ci.yml`], which contains a bunch of steps that are unified for all CI jobs that we execute. When a commit is pushed to a corresponding branch or a PR, the workflow executes the -[`calculate-job-matrix.py`] script, which dynamically generates the specific CI +[`ci.py`] script, which dynamically generates the specific CI jobs that should be executed. This script uses the [`jobs.yml`] file as an input, which contains a declarative configuration of all our CI jobs. @@ -299,8 +299,7 @@ platform’s custom [Docker container]. This has a lot of advantages for us: - We can avoid reinstalling tools (like QEMU or the Android emulator) every time thanks to Docker image caching. - Users can run the same tests in the same environment locally by just running - `src/ci/docker/run.sh image-name`, which is awesome to debug failures. Note - that there are only linux docker images available locally due to licensing and + `python3 src/ci/github-actions/ci.py run-local `, which is awesome to debug failures. Note that there are only linux docker images available locally due to licensing and other restrictions. The docker images prefixed with `dist-` are used for building artifacts while @@ -413,7 +412,7 @@ To learn more about the dashboard, see the [Datadog CI docs]. [GitHub Actions]: https://github.com/rust-lang/rust/actions [`jobs.yml`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/jobs.yml [`.github/workflows/ci.yml`]: https://github.com/rust-lang/rust/blob/master/.github/workflows/ci.yml -[`calculate-job-matrix.py`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/calculate-job-matrix.py +[`ci.py`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/ci.py [rust-lang-ci]: https://github.com/rust-lang-ci/rust/actions [bors]: https://github.com/bors [homu]: https://github.com/rust-lang/homu diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 17106be46744..459c082906eb 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -441,6 +441,46 @@ $ COMPILETEST_FORCE_STAGE0=1 x test --stage 0 tests/run-make/ Of course, some tests will not successfully *run* in this way. +#### Using rust-analyzer with `rmake.rs` + +Like other test programs, the `rmake.rs` scripts used by run-make tests do not +have rust-analyzer integration by default. + +To work around this when working on a particular test, temporarily create a +`Cargo.toml` file in the test's directory +(e.g. `tests/run-make/sysroot-crates-are-unstable/Cargo.toml`) +with these contents: + +
+Be careful not to add this `Cargo.toml` or its `Cargo.lock` to your actual PR! +
+ +```toml +# Convince cargo that this isn't part of an enclosing workspace. +[workspace] + +[package] +name = "rmake" +version = "0.1.0" +edition = "2021" + +[dependencies] +run_make_support = { path = "../../../src/tools/run-make-support" } + +[[bin]] +name = "rmake" +path = "rmake.rs" +``` + +Then add a corresponding entry to `"rust-analyzer.linkedProjects"` +(e.g. in `.vscode/settings.json`): + +```json +"rust-analyzer.linkedProjects": [ + "tests/run-make/sysroot-crates-are-unstable/Cargo.toml" +], +``` + #### Using Makefiles (legacy)
diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index b0527da7bf56..69f4c864186c 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -2,7 +2,9 @@ -> **FIXME(jieyouxu)** completely revise this chapter. + Directives are special comments that tell compiletest how to build and interpret a test. They must appear before the Rust source in the test. They may also @@ -248,10 +250,11 @@ Consider writing the test as a proper incremental test instead. |-------------|--------------------------------------------------------------|------------------------------------------|---------------------------| | `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `js-doc-test`, `rustdoc-json` | Any valid `rustdoc` flags | -> **FIXME(rustdoc)**: what does `check-test-line-numbers-match` do? -> -> Asked in -> . + ### Pretty printing diff --git a/src/doc/rustc-dev-guide/src/tests/docker.md b/src/doc/rustc-dev-guide/src/tests/docker.md index 31e3825f5673..a0aa8bd3e77b 100644 --- a/src/doc/rustc-dev-guide/src/tests/docker.md +++ b/src/doc/rustc-dev-guide/src/tests/docker.md @@ -45,6 +45,15 @@ Some additional notes about using the Docker images: containers. With the container name, run `docker exec -it /bin/bash` where `` is the container name like `4ba195e95cef`. +The approach described above is a relatively low-level interface for running the Docker images +directly. If you want to run a full CI Linux job locally with Docker, in a way that is as close to CI as possible, you can use the following command: + +```bash +python3 src/ci/github-actions/ci.py run-local +# For example: +python3 src/ci/github-actions/ci.py run-local dist-x86_64-linux-alt +``` + [Docker]: https://www.docker.com/ [`src/ci/docker`]: https://github.com/rust-lang/rust/tree/master/src/ci/docker [`src/ci/docker/run.sh`]: https://github.com/rust-lang/rust/blob/master/src/ci/docker/run.sh diff --git a/src/doc/rustc-dev-guide/src/traits/chalk.md b/src/doc/rustc-dev-guide/src/traits/chalk.md index 78deb367506c..844f42b9879f 100644 --- a/src/doc/rustc-dev-guide/src/traits/chalk.md +++ b/src/doc/rustc-dev-guide/src/traits/chalk.md @@ -10,7 +10,7 @@ stream and say hello! [Types team]: https://github.com/rust-lang/types-team [`#t-types`]: https://rust-lang.zulipchat.com/#narrow/stream/144729-t-types -The new-style trait solver is based on the work done in [chalk][chalk]. Chalk +The new-style trait solver is based on the work done in [chalk]. Chalk recasts Rust's trait system explicitly in terms of logic programming. It does this by "lowering" Rust code into a kind of logic program we can then execute queries against. @@ -30,7 +30,7 @@ You can read more about chalk itself in the ## Ongoing work The design of the new-style trait solving happens in two places: -**chalk**. The [chalk][chalk] repository is where we experiment with new ideas +**chalk**. The [chalk] repository is where we experiment with new ideas and designs for the trait system. **rustc**. Once we are happy with the logical rules, we proceed to diff --git a/src/doc/rustc-dev-guide/src/ty_module/early_binder.md b/src/doc/rustc-dev-guide/src/ty_module/early_binder.md index 69db189350fd..e8ff3a530780 100644 --- a/src/doc/rustc-dev-guide/src/ty_module/early_binder.md +++ b/src/doc/rustc-dev-guide/src/ty_module/early_binder.md @@ -47,10 +47,10 @@ fn bar(foo: Foo) { In the compiler the `instantiate` call for this is done in [`FieldDef::ty`] ([src][field_def_ty_src]), at some point during type checking `bar` we will wind up calling `FieldDef::ty(x, &[u32, f32])` in order to obtain the type of `foo.x`. -**Note on indices:** It is possible for the indices in `Param` to not match with what the `EarlyBinder` binds. For -example, the index could be out of bounds or it could be the index of a lifetime when we were expecting a type. -These sorts of errors would be caught earlier in the compiler when translating from a `rustc_hir::Ty` to a `ty::Ty`. -If they occur later, that is a compiler bug. +**Note on indices:** It is a bug if the index of a `Param` does not match what the `EarlyBinder` binds. For +example, if the index is out of bounds or the index index of a lifetime corresponds to a type parameter. +These sorts of errors are caught earlier in the compiler during name resolution where we disallow references +to generics parameters introduced by items that should not be nameable by the inner item. [`FieldDef::ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.FieldDef.html#method.ty [field_def_ty_src]: https://github.com/rust-lang/rust/blob/44d679b9021f03a79133021b94e6d23e9b55b3ab/compiler/rustc_middle/src/ty/mod.rs#L1421-L1426 diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 0f45c9dbd4e3..670e4bd1be68 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -60,16 +60,18 @@ - [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) - [mipsisa\*r6\*-unknown-linux-gnu\*](platform-support/mips-release-6.md) - [nvptx64-nvidia-cuda](platform-support/nvptx64-nvidia-cuda.md) - [powerpc-unknown-openbsd](platform-support/powerpc-unknown-openbsd.md) - [powerpc-unknown-linux-muslspe](platform-support/powerpc-unknown-linux-muslspe.md) - [powerpc64-ibm-aix](platform-support/aix.md) - [powerpc64le-unknown-linux-musl](platform-support/powerpc64le-unknown-linux-musl.md) - - [riscv32e*-unknown-none-elf](platform-support/riscv32e-unknown-none-elf.md) - - [riscv32i*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) + - [riscv32e\*-unknown-none-elf](platform-support/riscv32e-unknown-none-elf.md) + - [riscv32i\*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) - [riscv32im-risc0-zkvm-elf](platform-support/riscv32im-risc0-zkvm-elf.md) - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md) - [riscv64gc-unknown-linux-gnu](platform-support/riscv64gc-unknown-linux-gnu.md) @@ -78,14 +80,14 @@ - [s390x-unknown-linux-musl](platform-support/s390x-unknown-linux-musl.md) - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) - [sparcv9-sun-solaris](platform-support/solaris.md) - - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) + - [\*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) - [\*-nto-qnx-\*](platform-support/nto-qnx.md) - - [*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) - - [*-unknown-hermit](platform-support/hermit.md) - - [*-unknown-freebsd](platform-support/freebsd.md) + - [\*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) + - [\*-unknown-hermit](platform-support/hermit.md) + - [\*-unknown-freebsd](platform-support/freebsd.md) - [\*-unknown-netbsd\*](platform-support/netbsd.md) - - [*-unknown-openbsd](platform-support/openbsd.md) - - [*-unknown-redox](platform-support/redox.md) + - [\*-unknown-openbsd](platform-support/openbsd.md) + - [\*-unknown-redox](platform-support/redox.md) - [\*-unknown-uefi](platform-support/unknown-uefi.md) - [\*-uwp-windows-msvc](platform-support/uwp-windows-msvc.md) - [\*-wrs-vxworks](platform-support/vxworks.md) @@ -96,13 +98,14 @@ - [wasm32-unknown-unknown](platform-support/wasm32-unknown-unknown.md) - [wasm32v1-none](platform-support/wasm32v1-none.md) - [wasm64-unknown-unknown](platform-support/wasm64-unknown-unknown.md) + - [\*-win7-windows-gnu](platform-support/win7-windows-gnu.md) - [\*-win7-windows-msvc](platform-support/win7-windows-msvc.md) - [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md) - [x86_64-pc-solaris](platform-support/solaris.md) - [x86_64-unknown-linux-none.md](platform-support/x86_64-unknown-linux-none.md) - [x86_64-unknown-none](platform-support/x86_64-unknown-none.md) - [xtensa-\*-none-elf](platform-support/xtensa.md) - - [*-nuttx-\*](platform-support/nuttx.md) + - [\*-nuttx-\*](platform-support/nuttx.md) - [Targets](targets/index.md) - [Built-in Targets](targets/built-in.md) - [Custom Targets](targets/custom.md) diff --git a/src/doc/rustc/src/check-cfg.md b/src/doc/rustc/src/check-cfg.md index f7e831dc8bd8..c62ca9fd9ad6 100644 --- a/src/doc/rustc/src/check-cfg.md +++ b/src/doc/rustc/src/check-cfg.md @@ -134,7 +134,7 @@ As of `2025-01-02T`, the list of known names is as follows: - `unix` - `windows` -> Starting with CURRENT_RUSTC_VERSION, the `test` cfg is consider to be a "userspace" config +> Starting with 1.85.0, the `test` cfg is consider to be a "userspace" config > despite being also set by `rustc` and should be managed by the build-system it-self. Like with `values(any())`, well known names checking can be disabled by passing `cfg(any())` diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 7b07d9919921..deeabd810d3d 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -313,10 +313,12 @@ target | std | host | notes [`i686-unknown-redox`](platform-support/redox.md) | ✓ | | i686 Redox OS `i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI] [`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI] +[`i686-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [`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 @@ -332,6 +334,8 @@ target | std | host | notes `mipsel-unknown-linux-uclibc` | ✓ | | MIPS (LE) Linux with uClibc [`mipsel-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | 32-bit MIPS (LE), requires mips32 cpu support `mipsel-unknown-none` | * | | Bare MIPS (LE) softfloat +[`mips-mti-none-elf`](platform-support/mips-mti-none-elf.md) | * | | Bare MIPS32r2 (BE) softfloat +[`mipsel-mti-none-elf`](platform-support/mips-mti-none-elf.md) | * | | Bare MIPS32r2 (LE) softfloat [`mipsisa32r6-unknown-linux-gnu`](platform-support/mips-release-6.md) | ? | | 32-bit MIPS Release 6 Big Endian [`mipsisa32r6el-unknown-linux-gnu`](platform-support/mips-release-6.md) | ? | | 32-bit MIPS Release 6 Little Endian [`mipsisa64r6-unknown-linux-gnuabi64`](platform-support/mips-release-6.md) | ? | | 64-bit MIPS Release 6 Big Endian @@ -407,6 +411,7 @@ target | std | host | notes [`x86_64-unknown-trusty`](platform-support/trusty.md) | ? | | `x86_64-uwp-windows-gnu` | ✓ | | [`x86_64-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | +[`x86_64-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 64-bit Windows 7 support [`x86_64-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 64-bit Windows 7 support [`x86_64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`x86_64h-apple-darwin`](platform-support/x86_64h-apple-darwin.md) | ✓ | ✓ | macOS with late-gen Intel (at least Haswell) 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/rustc/src/platform-support/mips-mti-none-elf.md b/src/doc/rustc/src/platform-support/mips-mti-none-elf.md new file mode 100644 index 000000000000..731f0a8c42f1 --- /dev/null +++ b/src/doc/rustc/src/platform-support/mips-mti-none-elf.md @@ -0,0 +1,31 @@ +# `mips*-mti-none-elf` + +**Tier: 3** + +MIPS32r2 baremetal softfloat, Big Endian or Little Endian. + +- mips-mti-none-elf +- mipsel-mti-none-elf + +## Target maintainers + +- YunQiang Su, `syq@debian.org`, https://github.com/wzssyqa + +## Background + +These 2 targets, aka mips-mti-none-elf and mipsel-mti-none-elf, are for +baremetal development of MIPS32r2. The lld is used instead of Gnu-ld. + +## Requirements + +The target only supports cross compilation and no host tools. The target +supports `alloc` with a default allocator while only support `no-std` development. + +The vendor name `mti` follows the naming of gcc to indicate MIPS32r2. + +## Cross-compilation toolchains and C code + +Compatible C code can be built for this target on any compiler that has a MIPS32r2 +target. On clang and ld.lld linker, it can be generated using the +`-march=mips`/`-march=mipsel`, `-mabi=32` with llvm features flag +`features=+mips32r2,+soft-float,+noabicalls`. diff --git a/src/doc/rustc/src/platform-support/win7-windows-gnu.md b/src/doc/rustc/src/platform-support/win7-windows-gnu.md new file mode 100644 index 000000000000..180a1dc6d265 --- /dev/null +++ b/src/doc/rustc/src/platform-support/win7-windows-gnu.md @@ -0,0 +1,48 @@ +# \*-win7-windows-gnu + +**Tier: 3** + +Windows targets continuing support of Windows 7. + +Target triples: +- `i686-win7-windows-gnu` +- `x86_64-win7-windows-gnu` + +## Target maintainers + +- @tbu- + +## Requirements + +This target supports all of core, alloc, std and test. Host +tools may also work, though those are not currently tested. + +Those targets follow Windows calling convention for extern "C". + +Like any other Windows target, the created binaries are in PE format. + +## Building the target + +You can build Rust with support for the targets by adding it to the target list in config.toml: + +```toml +[build] +build-stage = 1 +target = ["x86_64-win7-windows-gnu"] +``` + +## Building Rust programs + +Rust does not ship pre-compiled artifacts for this target. To compile for this +target, you will either need to build Rust with the target enabled (see +"Building the target" above), or build your own copy by using `build-std` or +similar. + +## Testing + +Created binaries work fine on Windows or Wine using native hardware. Remote +testing is possible using the `remote-test-server` described [here](https://rustc-dev-guide.rust-lang.org/tests/running.html#running-tests-on-a-remote-machine). + +## Cross-compilation toolchains and C code + +Compatible C code can be built with gcc's `{i686,x86_64}-w64-mingw32-gcc`. diff --git a/src/doc/rustc/src/platform-support/win7-windows-msvc.md b/src/doc/rustc/src/platform-support/win7-windows-msvc.md index 45b00a2be829..77b7d68212bf 100644 --- a/src/doc/rustc/src/platform-support/win7-windows-msvc.md +++ b/src/doc/rustc/src/platform-support/win7-windows-msvc.md @@ -1,8 +1,12 @@ -# *-win7-windows-msvc +# \*-win7-windows-msvc **Tier: 3** -Windows targets continuing support of windows7. +Windows targets continuing support of Windows 7. + +Target triples: +- `i686-win7-windows-msvc` +- `x86_64-win7-windows-msvc` ## Target maintainers 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/doc/unstable-book/src/compiler-flags/min-function-alignment.md b/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md new file mode 100644 index 000000000000..b7a3aa71fc4c --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md @@ -0,0 +1,24 @@ +# `min-function-alignment` + +The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/82232. + +------------------------ + +The `-Zmin-function-alignment=` flag specifies the minimum alignment of functions for which code is generated. +The `align` value must be a power of 2, other values are rejected. + +Note that `-Zbuild-std` (or similar) is required to apply this minimum alignment to standard library functions. +By default, these functions come precompiled and their alignments won't respect the `min-function-alignment` flag. + +This flag is equivalent to: + +- `-fmin-function-alignment` for [GCC](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fmin-function-alignment_003dn) +- `-falign-functions` for [Clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang1-falign-functions) + +The specified alignment is a minimum. A higher alignment can be specified for specific functions by using the [`repr(align(...))`](https://github.com/rust-lang/rust/issues/82232) feature and annotating the function with a `#[repr(align())]` attribute. The attribute's value is ignored when it is lower than the value passed to `min-function-alignment`. + +There are two additional edge cases for this flag: + +- targets have a minimum alignment for functions (e.g. on x86_64 the lowest that LLVM generates is 16 bytes). + A `min-function-alignment` value lower than the target's minimum has no effect. +- the maximum alignment supported by rust (and LLVM) is `2^29`. Trying to set a higher value results in an error. diff --git a/src/doc/unstable-book/src/language-features/default-field-values.md b/src/doc/unstable-book/src/language-features/default-field-values.md new file mode 100644 index 000000000000..3143b2d7cae3 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/default-field-values.md @@ -0,0 +1,93 @@ +# `default_field_values` + +The tracking issue for this feature is: [#132162] + +[#132162]: https://github.com/rust-lang/rust/issues/132162 + +The RFC for this feature is: [#3681] + +[#3681]: https://github.com/rust-lang/rfcs/blob/master/text/3681-default-field-values.md + +------------------------ + +The `default_field_values` feature allows users to specify a const value for +individual fields in struct definitions, allowing those to be omitted from +initializers. + +## Examples + +```rust +#![feature(default_field_values)] + +#[derive(Default)] +struct Pet { + name: Option, // impl Default for Pet will use Default::default() for name + age: i128 = 42, // impl Default for Pet will use the literal 42 for age +} + +fn main() { + let a = Pet { name: Some(String::new()), .. }; // Pet { name: Some(""), age: 42 } + let b = Pet::default(); // Pet { name: None, age: 42 } + assert_eq!(a.age, b.age); + // The following would be a compilation error: `name` needs to be specified + // let _ = Pet { .. }; +} +``` + +## `#[derive(Default)]` + +When deriving Default, the provided values are then used. On enum variants, +the variant must still be marked with `#[default]` and have all its fields +with default values. + +```rust +#![feature(default_field_values)] + +#[derive(Default)] +enum A { + #[default] + B { + x: i32 = 0, + y: i32 = 0, + }, + C, +} +``` + +## Enum variants + +This feature also supports enum variants for both specifying default values +and `#[derive(Default)]`. + +## Interaction with `#[non_exhaustive]` + +A struct or enum variant marked with `#[non_exhaustive]` is not allowed to +have default field values. + +## Lints + +When manually implementing the `Default` trait for a type that has default +field values, if any of these are overriden in the impl the +`default_overrides_default_fields` lint will trigger. This lint is in place +to avoid surprising diverging behavior between `S { .. }` and +`S::default()`, where using the same type in both ways could result in +different values. The appropriate way to write a manual `Default` +implementation is to use the functional update syntax: + +```rust +#![feature(default_field_values)] + +struct Pet { + name: String, + age: i128 = 42, // impl Default for Pet will use the literal 42 for age +} + +impl Default for Pet { + fn default() -> Pet { + Pet { + name: "no-name".to_string(), + .. + } + } +} +``` diff --git a/src/etc/installer/msi/rust.wxs b/src/etc/installer/msi/rust.wxs index 2d155bf0b101..f29e1e4d27a2 100644 --- a/src/etc/installer/msi/rust.wxs +++ b/src/etc/installer/msi/rust.wxs @@ -172,11 +172,6 @@ - - - - - @@ -284,41 +279,7 @@ - - - - - - - - - - - - - - - + diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 8aeebdde7bb4..99e88f878fba 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -314,12 +314,13 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { PatKind::Box(p) => return name_from_pat(p), PatKind::Deref(p) => format!("deref!({})", name_from_pat(p)), PatKind::Ref(p, _) => return name_from_pat(p), - PatKind::Lit(..) => { + PatKind::Expr(..) => { warn!( - "tried to get argument name from PatKind::Lit, which is silly in function arguments" + "tried to get argument name from PatKind::Expr, which is silly in function arguments" ); 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/stage0 b/src/stage0 index 23ee3304b422..bd9a753c2115 100644 --- a/src/stage0 +++ b/src/stage0 @@ -14,456 +14,468 @@ nightly_branch=master # All changes below this comment will be overridden the next time the # tool is executed. -compiler_date=2024-11-27 +compiler_date=2025-01-08 compiler_version=beta -rustfmt_date=2024-11-27 +rustfmt_date=2025-01-08 rustfmt_version=nightly -dist/2024-11-27/rustc-beta-aarch64-apple-darwin.tar.gz=58e2ba5f6a388a5bc9ecfa1c7ec4ba3efd9662e1875ea5fba484c75c5f930227 -dist/2024-11-27/rustc-beta-aarch64-apple-darwin.tar.xz=95f3ed14a6e3093cdee83f80227c7966783fffcc0ff0be6ef095df0dc744b9f8 -dist/2024-11-27/rustc-beta-aarch64-pc-windows-msvc.tar.gz=687b58e25baa685be9357048cc1e0c469c25dc6530db55e0179234615ff4d289 -dist/2024-11-27/rustc-beta-aarch64-pc-windows-msvc.tar.xz=694e2405efb25c53575599ad6573d8c4646a168e5d4bde922ef5e7d43f3e530c -dist/2024-11-27/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=4b42d00e701f90c1fff9a692f1bc1da957fcadbd1813c3066dc1f1990bceadcc -dist/2024-11-27/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=4f83a8cced878c03aea2d6cec7e1d588c2b6590850664f28f3b49a3be3a8b827 -dist/2024-11-27/rustc-beta-aarch64-unknown-linux-musl.tar.gz=bae97ca3778fbaa40f6db4da4f6da0b2bb870e9880165fc3126363f55a1fb23a -dist/2024-11-27/rustc-beta-aarch64-unknown-linux-musl.tar.xz=3e617e866a8c03fef243dede8c086d085a6564aeeb5570bada62db5a16e95f2a -dist/2024-11-27/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=96d2f23814db8286de9ffce7bab033aa4dc656f83aa39418c8f877a5befbb100 -dist/2024-11-27/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=0433a866c4d8a434990746216232759345d3ef556b0b92358b231fc1e727962e -dist/2024-11-27/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=6a776c6eb2b7b4992eed6a127d11b8845695cdc5d23938ced2267f88f8d97033 -dist/2024-11-27/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=2649e0f522755dcd511a6459258c3c3a24808fc49d3513f3d9098b17bd0f2c79 -dist/2024-11-27/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=02a65e970ee930b46c2d10e1eb3d2e6f2420e08bbef039f10a8311baf57f274f -dist/2024-11-27/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=3843610c7ecf98948a5f78a364c223d520a084bc8c49ec0c3c8ec48bad6c0bf8 -dist/2024-11-27/rustc-beta-i686-pc-windows-gnu.tar.gz=75288ca1e65157b8902195ec2fff31015017c90d3b8f51890f2f851529b200cb -dist/2024-11-27/rustc-beta-i686-pc-windows-gnu.tar.xz=5c694e50b41cd6f2c7b59b891eda3e728a1a2c56617b96c9da001b35885637b4 -dist/2024-11-27/rustc-beta-i686-pc-windows-msvc.tar.gz=6d1a66c090322ccf272a425c779f9165398e6bd9e0c7e5d0a5f262086d43ed88 -dist/2024-11-27/rustc-beta-i686-pc-windows-msvc.tar.xz=e826fe8be287f8969d11f620da9d01ea0d913eba145fb79ae86bc78674f1b919 -dist/2024-11-27/rustc-beta-i686-unknown-linux-gnu.tar.gz=31e0d9a1ff97d92c391cc913fd865a0c8afd614279654350d469bdf881a1b1a3 -dist/2024-11-27/rustc-beta-i686-unknown-linux-gnu.tar.xz=3b11a6be4863cb6ad420b66558b7745431834d1fac5dbda899a384dc30b526cf -dist/2024-11-27/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=59d96cd092a56d12ea796b65225479803edadbdcb483effdfa36e191f73483f1 -dist/2024-11-27/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=0714a469cbd7d96118e9260f80fedf34b21234776904e9c8ec5b71c9493dd36a -dist/2024-11-27/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=25113ca9719a036ae581f68df4dda74f1446b49ababe7bb194510fbd1b296e7a -dist/2024-11-27/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=25bd910ea722fe2d4a1e66089352c92f97759ec7212af2b9a04431b469ed8e12 -dist/2024-11-27/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=7a83c9f63d88849c4d691ad1d7fbe7ab5924b5a7d76378ddff5024269be95310 -dist/2024-11-27/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=fbbb11b4c986186d94b0e00ebc70400d0071efc2ba3b802abdd877d86b8cacdd -dist/2024-11-27/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=6223dff24490e3e8506dfbe2b12620155d7c05fce92191cb01f3b28e269bf1be -dist/2024-11-27/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=6fb1edea73bcb76d9cb6061d1aba15ee4655a212c1bf96328a225c7c05df3462 -dist/2024-11-27/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=0b7bc9ec0c40a6203fb62eb71fb6eeab43a4f64a2f7d0d51a1e0d041a216adb1 -dist/2024-11-27/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=d77a49acbf26b8c9a2a5f79fdc640feb1c81b9fa38f442b81f69127f49c7cde7 -dist/2024-11-27/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=bdf45a37fad10df4822690ea73840b38ee3e1048878982e625efbad3b2054f28 -dist/2024-11-27/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=ad6a2f03b2e27711e1ec4eb75502782c4bc0200cfaca2b5246e0bd5b86bf29ad -dist/2024-11-27/rustc-beta-s390x-unknown-linux-gnu.tar.gz=b0cc3024a717d33775dbea63e83f317f8826e564ceb4ecb7a6cf23676f66ccdc -dist/2024-11-27/rustc-beta-s390x-unknown-linux-gnu.tar.xz=9d5b44a82803d5de5355ffced6ec1dcb9860950910bba6dc54878ad4da7ff444 -dist/2024-11-27/rustc-beta-x86_64-apple-darwin.tar.gz=c7174c35b530453182f5f0b15c6684c030a2ff2770f4cc1a4a4ba75695e53b99 -dist/2024-11-27/rustc-beta-x86_64-apple-darwin.tar.xz=c68c26159a3fb4d4380fb25488aa78223989893d7786b8ec947d82ce0cc905bc -dist/2024-11-27/rustc-beta-x86_64-pc-windows-gnu.tar.gz=c97caf7877097d78fe2191e715e3a45d827d28c27b4dabf0747f8ca522ee43f5 -dist/2024-11-27/rustc-beta-x86_64-pc-windows-gnu.tar.xz=b7794ecabc42698cb77c1c90d66676a8b1b3632108ab95954ee496ee7d6e5708 -dist/2024-11-27/rustc-beta-x86_64-pc-windows-msvc.tar.gz=28068a6e050abe21933920dba3ead2261f7d0c09687d5a86a5db51cca6e72ea9 -dist/2024-11-27/rustc-beta-x86_64-pc-windows-msvc.tar.xz=3860d7ec8564019c79551727a3a54c66453a7d654fbb89b8feeae75d9021fa77 -dist/2024-11-27/rustc-beta-x86_64-unknown-freebsd.tar.gz=6c2a918f8d40ba01e648565c98ea120d32562dd93820e8f2e33abb2aef08e1da -dist/2024-11-27/rustc-beta-x86_64-unknown-freebsd.tar.xz=5d1edcc6c4be49849c3c2d1c3c173d4a127db3cfb615093e5ee396e0d0c77366 -dist/2024-11-27/rustc-beta-x86_64-unknown-illumos.tar.gz=a64c30d82dca58d09bfa38e72bbe1457b48b101157193a3ee33dd85922bbfe9d -dist/2024-11-27/rustc-beta-x86_64-unknown-illumos.tar.xz=87d14b146386fe1f9e24fedc25e1bf9682913bbd7537047b0baef0d74cad5473 -dist/2024-11-27/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=3f186913535ac59446e249f4d3e35e3d93a3f8207b31ee31b3f70af8344856bc -dist/2024-11-27/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=0c17a60ebac74a9cdfc2ec69fd71a21e370ff178f4c83be6020642b55b6d78ab -dist/2024-11-27/rustc-beta-x86_64-unknown-linux-musl.tar.gz=327973698dd0ffe9b2d56b3b5929f82ec552f2002b4113fc9a738e27b0005ac0 -dist/2024-11-27/rustc-beta-x86_64-unknown-linux-musl.tar.xz=d7e2f768922ae36a9eb302667398ee308c13f829c03235682529fce90253813d -dist/2024-11-27/rustc-beta-x86_64-unknown-netbsd.tar.gz=13f327974170a0423ea6e7241cd3a454ab8c39b657a707c08f2e306a7a657b45 -dist/2024-11-27/rustc-beta-x86_64-unknown-netbsd.tar.xz=5c75388802c0ddcfa51336883f4039b6f7963e0efbe47c209bb455c12951990b -dist/2024-11-27/rust-std-beta-aarch64-apple-darwin.tar.gz=84c4c02fdecc669c5d26c49120fdece5899133c098f5ef791ea19aba33936dea -dist/2024-11-27/rust-std-beta-aarch64-apple-darwin.tar.xz=7ea047840073e6ad3123a61ca799bc56c951edbb71cb8495ecbc2029759a0190 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios.tar.gz=dfb8207b4b3bc13a9704fd1d212d92a22a0e7a57293f24f28bcaa846ad4c08b6 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios.tar.xz=96940a84d95b4cfdde84a501758c9dd83f5d6cb5afce9142917d5cb3dd75c040 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=4ed66daf59e9a8b61d66caef4b2248be72049282518d7526be771301bee2b905 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=013d59e32097917b269c45a837bff380a41c4f6401b0c145264cca5a1bbd91c3 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios-sim.tar.gz=270a0322dd3c2342745faa8797f4f64da8c0028b322385d209ff65cf04965a47 -dist/2024-11-27/rust-std-beta-aarch64-apple-ios-sim.tar.xz=9d54f7f51c99f17ba9cfe7cbe005ddbfe598f8af6df1dfccc9ef37029cc08b41 -dist/2024-11-27/rust-std-beta-aarch64-linux-android.tar.gz=9ae4c1d6044b2c3586791f127108e308e0a381e8e14ecc20b3830ae5258a03c2 -dist/2024-11-27/rust-std-beta-aarch64-linux-android.tar.xz=689225ea098a88a88e8e2269ef53f964405a6c31b3ee2bda0fef98da4ed9178e -dist/2024-11-27/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=f1d9c996cbe468ca93b15c76b84c544930d7452f1eff2968a298c885203f14cb -dist/2024-11-27/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=f3f8c088d74c81b2457ddff57008cb908cc86e16d0fac60d10969e45210d88d6 -dist/2024-11-27/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=fb4e5d66911a47bd63429686885cd5c7f0a6310b25622f36bb43a754300526be -dist/2024-11-27/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=ab6c7d346bfd72f06fbbc198743bda35fcb7fd1e131c05c54935b6ceff282f17 -dist/2024-11-27/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=32b2e0d5161f7d8354d846c91e5cb25530e78351dfcff4ebf0a6fba669e2c483 -dist/2024-11-27/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=656fc4a8af570b28b3f83cd59d4e5226f15d11b5d8c26b58ca2c1b2a4e18c4df -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=13c98efcde78c1e187f728509f2950a0a407ba32e730877cb9ca4cbb4ba01469 -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=6372891715046375fd0a04e80c95bf79080db2d136e187b02ea96647e9281407 -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=db156e0d5db6f04621e625e92323a04091c049a8b5e2b4f7e0fb2e50b81ab2ec -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=b610214bb59b8f58be96b84b0f13e5f50473e21039f6e827fa16c446d0e9ccba -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=99f96b0bda31bf21853c96b3618408587f275f46ea9bd7d1fc741e58343a5625 -dist/2024-11-27/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=dfa1f209f546b457aa6ef742a645f00f55c70fde86522c737f7a560494170157 -dist/2024-11-27/rust-std-beta-aarch64-unknown-none.tar.gz=4735b29991a2337a7e2016c5a827635e17ac149372efafc0442a86b0dc0759fb -dist/2024-11-27/rust-std-beta-aarch64-unknown-none.tar.xz=fead0635c572aa4f5e5564e497f1b7e892217ebe52fea54aedacbefb32f6343d -dist/2024-11-27/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=bbb9b6fedb874a61d99d22b4cdb2d20d8b9e6c69924922312692aa526851b960 -dist/2024-11-27/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=9e0d48236676dc9947565daf0c2605316c5b4abc06bdb1c343559c46b9c4e304 -dist/2024-11-27/rust-std-beta-aarch64-unknown-uefi.tar.gz=e230ffa3d46dbe5330416cdf8b7235b8dc2cf119ef8990b422eeebd017ebd17f -dist/2024-11-27/rust-std-beta-aarch64-unknown-uefi.tar.xz=38a5560575740f92cb8664d204924cf7a2972e0f2f396852d8a22befd2cdd848 -dist/2024-11-27/rust-std-beta-arm-linux-androideabi.tar.gz=0b0a9f95d10484207c52fcd8ab1bd56903b53f18ce46fe263d5b7bb085044246 -dist/2024-11-27/rust-std-beta-arm-linux-androideabi.tar.xz=91f7655ab2bce397a0821edc0f523dbf2bac0af39a50ead106eb1de637ec5c44 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=e8210f03bc18661954f147dedd0b4af721d005866c0c4fea1e91594cd6c9bc2b -dist/2024-11-27/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=56a623817e6a600e802727d59d4cd653e84cb628695e39ecb23ae99065113115 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=310a408bde58335d8011af480d4f8c38745865772d71631596cef1a79f9f4fc2 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=fd6766ea9e828adc2e43ff1654a459785eed05ad00c7f6804c742ff8ae8f6b8a -dist/2024-11-27/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=e663e30526d85d89f41456a3d5bcd411397cb69e7cb9508aa1472b015963fd18 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=0361504ada464a7d21e7ac66fdf9e1e820695f3a8ca4854695e78a748c5c7a18 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=c375be97e626900d809390c61bf6f2d689c9fca8e58c85e4e02b694bed4c5ce0 -dist/2024-11-27/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=c33e3b4f9098860f75fcc9a8f86c7e7173692ad4c19cbb2c0de068961f87c192 -dist/2024-11-27/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=3f50d9afd9df4db85958e4575429dd4c80b6702a8d91c3f1d9327345aaadefe0 -dist/2024-11-27/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=0016d3a6ec8351ee9fac2eeae358a0854a3c2b360822bb3ba670255e8f27f57e -dist/2024-11-27/rust-std-beta-armebv7r-none-eabi.tar.gz=f23117b972ccd21c0a6f4db4b544401045c9f157595a297fb56d7e864cf190fe -dist/2024-11-27/rust-std-beta-armebv7r-none-eabi.tar.xz=ab9be4bb25b1575af276cbec7fb1a1f8c683741b0f137bca7b4b61ab13575645 -dist/2024-11-27/rust-std-beta-armebv7r-none-eabihf.tar.gz=673561456e26bc992cb6a888c34c0b6b13e1bd0d3e0764360a785a6c3369f0d0 -dist/2024-11-27/rust-std-beta-armebv7r-none-eabihf.tar.xz=29c6f01c636bcd901b88e92c60874c454369efd41f690d74b1fa2adf3450166a -dist/2024-11-27/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=c214d4439676a19c057324dc736d63aa0328ac74c0eb9ced8d782d8bc89757ae -dist/2024-11-27/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=4baf85ac632750c35d007bcae8f23720208c5197f4a1d49b0a85b2b0e137f603 -dist/2024-11-27/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=a7e81c0e80f2508c81ba9d8e658dabeb19af2de6f7365f6e377d39e99d8449e7 -dist/2024-11-27/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=3f37f746aaa03107573a3a2b201c15bf86777ec405dc03640e7d3f24f718f67f -dist/2024-11-27/rust-std-beta-armv7-linux-androideabi.tar.gz=fe7c7ecb8a8e612caf0e7ecf04d18ccaed31d8b93911ded6b3cba0c78fcbbddc -dist/2024-11-27/rust-std-beta-armv7-linux-androideabi.tar.xz=d571b714821de4b7d2accad221b5e043e7f9aead8dbc06586f4f7ff3f3f3e38e -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=663396f0e4c45bfc041b1a502b64627620f499ca6343664633761c3fb1c2229e -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=4d8c47d74958e3b1058ab721e9c6635774afad0252f251b0c9fe4f0895d0c81e -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=3cba82809bc1204b6e8f62877c5e9f9a08f111da7d439032cc30d55be0e88fd7 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=d525a4dde496e72fd18c6f2a525756d652ced436007f513267e9201d8b1c0a40 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=ec0311a0722f205b117160817b2137e9a73f0f4e540930d61c831ceba1265f0d -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=c63c4a28225dfca26baab129f06d8537fae5710083046f203a2fb73c50b48671 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=52eae1bdf37366bf406cff95e5e7979d0651979ae5ad4c036e8badddc8ec8b82 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=542791fb5cd80fe5515ab0812c9fd8671f083151d4262fc76d26f7723ba961e1 -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=91697f75c762bdb51f19348953768a45bc37c389c2e904a56f4f092604ed2d1c -dist/2024-11-27/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=a79a7a5974b365f70598c413da3db8262d14414e3b4d48dde45f529bca4c6ef8 -dist/2024-11-27/rust-std-beta-armv7a-none-eabi.tar.gz=6eec497ec2aaf6c5beb8e6dfbeaf32190b6f7206aebffb7499b5fdd7e5e7643a -dist/2024-11-27/rust-std-beta-armv7a-none-eabi.tar.xz=234924a6746fed2efa112b0b805d0ca56252b6cde82e6422774871e9c009fed6 -dist/2024-11-27/rust-std-beta-armv7r-none-eabi.tar.gz=1b0cd107412fae22266267bd8eccd2b39a36afa8936da3888d0bd0d2f4cfdb1c -dist/2024-11-27/rust-std-beta-armv7r-none-eabi.tar.xz=0d0d1ee48f9032df88ea7b3f73f06300686f4d5b9fc16f1d4554caf8356cfd74 -dist/2024-11-27/rust-std-beta-armv7r-none-eabihf.tar.gz=72f0549de44925bd08286e092261164dac47826124123e3fe707411a3dfa756c -dist/2024-11-27/rust-std-beta-armv7r-none-eabihf.tar.xz=eb45acd55dd70be3670de83c8cbef5b6ef05827b3f97b7c71679a19c88f8f3e6 -dist/2024-11-27/rust-std-beta-i586-pc-windows-msvc.tar.gz=edd8dadbba618f2a786dfcc68e4a5c7f2b7cf1528d1c16ca6ddbc52622df95c6 -dist/2024-11-27/rust-std-beta-i586-pc-windows-msvc.tar.xz=ae59c5d77b31aeac30c22104abc0abda6b7f725419e9f86db6cae634ededf262 -dist/2024-11-27/rust-std-beta-i586-unknown-linux-gnu.tar.gz=8996c94269e4c60270d253ea75bddb9482facc441ee2314a387888924bea1d92 -dist/2024-11-27/rust-std-beta-i586-unknown-linux-gnu.tar.xz=f6b59e3d6e7f0e458c048ad4aad9a2da23f3b54aeac20a905d2e00379da1bd6a -dist/2024-11-27/rust-std-beta-i586-unknown-linux-musl.tar.gz=85f22b148be72691214dee620eac582405dc3b8937d2ef7c480c9070b963af3a -dist/2024-11-27/rust-std-beta-i586-unknown-linux-musl.tar.xz=4b48538541dd20fad8569cb3f0994d5ce6aceaacdd8b319d00b0234cefc5c267 -dist/2024-11-27/rust-std-beta-i686-linux-android.tar.gz=6e5ce19fc4cd8cf4dbaca8b31f4032b08ee413ce20f354186e4945efd4e8b6af -dist/2024-11-27/rust-std-beta-i686-linux-android.tar.xz=5e78b98abdf2b7348381863052c109beec025a5f68285ce72e6f6e9245c22ce3 -dist/2024-11-27/rust-std-beta-i686-pc-windows-gnu.tar.gz=50057f990d1127e7d693b1c5b9539b19262a478130bed51dbc2837d61d2db6da -dist/2024-11-27/rust-std-beta-i686-pc-windows-gnu.tar.xz=ea524b276e42a1cb918625df12b8d3397ada5ecd43bb9ad5c676747e1f916c8b -dist/2024-11-27/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=c7c9093e277187590b86ea02fcd940071c8bce93a894396d73953276ec58eb1d -dist/2024-11-27/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=a67879ff0dcc591d30a155fd972149953eaac3b139bec9aca3a383dc277ddbc6 -dist/2024-11-27/rust-std-beta-i686-pc-windows-msvc.tar.gz=a086c8272f9c7c0db3d5de37b027478dfc22fc577c613254a821700c645df3c9 -dist/2024-11-27/rust-std-beta-i686-pc-windows-msvc.tar.xz=8ac4b938e0b643e47d6d0ba6e5283887fcc73cb1a825fc065c35879b085d9c28 -dist/2024-11-27/rust-std-beta-i686-unknown-freebsd.tar.gz=3614b95b54ca24c598e3ed6e14aaa86f7048c2a5be2b3e9f97d9ca00f3d890ce -dist/2024-11-27/rust-std-beta-i686-unknown-freebsd.tar.xz=6d36127f5c113b01300fa0d54062bc1137b51d2514346c8d84bafb9f2c56a972 -dist/2024-11-27/rust-std-beta-i686-unknown-linux-gnu.tar.gz=a6d33a96eeb667f3f4649ea6ccdd6212a183bce85f095ff7db5036c3a50d35a6 -dist/2024-11-27/rust-std-beta-i686-unknown-linux-gnu.tar.xz=bab1dada484d61bb96e2834da5fc6ed5d4d788e6636849e9802f9fb6bff45d55 -dist/2024-11-27/rust-std-beta-i686-unknown-linux-musl.tar.gz=0ac331a1c9e384430a5f316809bffdf01bae3ae9cc1f807f07142ffde7388602 -dist/2024-11-27/rust-std-beta-i686-unknown-linux-musl.tar.xz=136341bbf4c820dfc98e96cee41fa0e9fcbc7d81d2772407efaf1d4be6d10f7f -dist/2024-11-27/rust-std-beta-i686-unknown-uefi.tar.gz=32f06ca3b2752a8687e2f282a59823671fcec672bffc0775a9fb27da50b6bb11 -dist/2024-11-27/rust-std-beta-i686-unknown-uefi.tar.xz=b2f376089b39a34f3308833ce9bf65200f9665e5c55bbda3e5b7e9c5096b7e67 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=a2ca950893c93be71c7db707230b98674a29c013546e27d95bdf8c3c6eebf9a7 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=c70b58f4583578347e9f0fa20d17bd5fc51d88717095e05d73091209f2a363be -dist/2024-11-27/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=ed284c40b6253c022e39718dc2af2e4100ca7e805d35d86aca04937586f26ece -dist/2024-11-27/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=3efcabf46e0d02505ebf0926c6c14e0c70838f9b510f2328ccd98dd0fa977794 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-none.tar.gz=9b9f8418fdf64fef652f15b28a1a1f2f8c63c18da8b323994f10f921010721ab -dist/2024-11-27/rust-std-beta-loongarch64-unknown-none.tar.xz=fdc673e4b250424c6dafc18a165ffb592aecd7b897e483af8ca54aa145a0e6a9 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=802d22c1c364a58402f9313c6ba2f58b79f3cf3163842a608b78680fa2b3d476 -dist/2024-11-27/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=dc4932d91d8a2887d309dbd6317d6c41afd083043f44742138ca7c97f60b931c -dist/2024-11-27/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=636b19ca036fc7ddcf358062c98f6bcf31e8c2cd035cbb9dc0a6b96c81217f7c -dist/2024-11-27/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=31f80fc50be95bf665d21fe52b714532ff34ebfcdc0c975bb68f7d2f2cebd0e0 -dist/2024-11-27/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=f2c83275df652bc5b387525e77a1aa30bac31fb301b304ddd7e6bf402e140c34 -dist/2024-11-27/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=41358bdb20fe3a97a95fd7b656b1ed03d5b8012354d8cfad2437bc9ef6c7aeb6 -dist/2024-11-27/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=49b8f286b4abb3b34bd38c92ea3e15efbd1f623d5efa7bcff10711b595363579 -dist/2024-11-27/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=283f065701c77fa47a2d5413d20fa40989fbbcc75bc938889de6c4f9b6b9e53e -dist/2024-11-27/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=d0ee5ba5dd6f94dc6ca981be0d55f2e3f51c2af25877131a7a74e0ab9848dc3d -dist/2024-11-27/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=79c78bb31022ec8e7a3fad9823c5d22ac553d389104844110473d1877d8c8302 -dist/2024-11-27/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=b953686e3034f2b51033a2630328c8eb5f59fdf8ef4e26189bddb8efb4c482cf -dist/2024-11-27/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=fb25ac2e68594d379f915f0555b3ded2b1a7b5a67144e12eb62949f540b0d957 -dist/2024-11-27/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=fbf5a16a57de3a7e846389aebe18bc7871f43e67eff39fc9b4744b5a445b2639 -dist/2024-11-27/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=55a92aa5770924a21b706724c15f58d66ef2ca5b630f7025f6ba1416152ef350 -dist/2024-11-27/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=b880b7209ea604761ad2a46832925d32fc547737b879645fd3cf99ec235e7b39 -dist/2024-11-27/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=c48cc6a6fda06fab014c0b8fef49006dd32fb71f8e981ba44a0c25d47fbfe034 -dist/2024-11-27/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=7d133b6c8d0e8b8ec8b65a74ed8d64a22e2fa5e4f6d4a1e36f9e0ebb8826f40e -dist/2024-11-27/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=1c507e003c38115bfffe8701df1ff7aed6ad16ef5ac4b383f84bd2d6aa0216bd -dist/2024-11-27/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=111a909b3d1a04b328ca1c1f247ee4eaaa84349eeaa8df77f7901bbc0efc94c1 -dist/2024-11-27/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=d29f92ed071c60259feaa8005eb5392da549f8e2069fecb1b77e112f89cf1bbc -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=241b1ec9ef6a89277944c9d8021f521284dc28822c5f80513b6cd94d2bdd1d64 -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=9f5b02be846fa2084becc5616f024094d47beca102442ac7a0e3588e0f12ee7e -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=7272eae1625415c0f57bf541dd62c14e02b9946ca1de04615827a1f5218c2181 -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=d4bcd0068d3c9e7351565fec57ca68d36c9ddbfe59249a6bd2c56930ebad2257 -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=758ccc47004398add2e3cfef52e4839e14dc4d132a215658e576e590401a28ca -dist/2024-11-27/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=6902fee1027b0136f8dc5179e69e24ed9e34366e9409129b9ba223043ddadea1 -dist/2024-11-27/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=3024570f3e382a57fce0e832c52d080a83d42206d8412b724361bd1f0087ec4d -dist/2024-11-27/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=20eaa72b6fa32b9884b43030a0bee554bafa9de3f3b51c5768ba0ff47ec492c1 -dist/2024-11-27/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=a3eae70be3fa5b0a56cc88cc6433cdb7c42b3b974caf0d879d21a5219f4f826e -dist/2024-11-27/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=1da88d1882d4dd1f15ca97e016f392a01b56951882ae75929023e83fa557d796 -dist/2024-11-27/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=387cc8ccaeed6f9c6a8c9d21004e45ac97a9739643a63448eb893e13ed4df22d -dist/2024-11-27/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=1b9afe3631fb7847b2a8a8f483fc00ca203ccc18ae298339801dab1c7b06b3b3 -dist/2024-11-27/rust-std-beta-sparcv9-sun-solaris.tar.gz=f9a4d2115d6fce06e0da7a9f579b197d383034c218573e145b3c7dae0aad21d4 -dist/2024-11-27/rust-std-beta-sparcv9-sun-solaris.tar.xz=c352bc631e538d4e68eaaccdfb9dca1c9dc8ff0ef4a75d8ee91016aada39d717 -dist/2024-11-27/rust-std-beta-thumbv6m-none-eabi.tar.gz=d97851ca9193d98192a55aeaf695973ac706e6ba3e198643c200cf410817b424 -dist/2024-11-27/rust-std-beta-thumbv6m-none-eabi.tar.xz=89a6524a3743e5a769a583b4a6bf8a76ef5b45c67b7e39da7ae24b687bed4943 -dist/2024-11-27/rust-std-beta-thumbv7em-none-eabi.tar.gz=6fe614db166618a8f2ccb5807e02884daa8bd8dfded2164f88d79fc688047020 -dist/2024-11-27/rust-std-beta-thumbv7em-none-eabi.tar.xz=91c5e4f4575ab004119797988679077e5057881ad9a7f6790b673dd062a8b7ee -dist/2024-11-27/rust-std-beta-thumbv7em-none-eabihf.tar.gz=d0e05467fb1562ed7d6687a50b7fafa14ba0c2aad328f0e7c19eaca1ee366475 -dist/2024-11-27/rust-std-beta-thumbv7em-none-eabihf.tar.xz=0473372df5fece49e58efcd3b2a702bb93e47ec149b38fd5244ad667804e7c63 -dist/2024-11-27/rust-std-beta-thumbv7m-none-eabi.tar.gz=e6b6130a1a571325cc1bda8ddba6b2672104c2feeed5c93c6022bfcc8ba4b247 -dist/2024-11-27/rust-std-beta-thumbv7m-none-eabi.tar.xz=d4c739c42104e04aba55b96a3ad434e11b013ba44649dd3a648cfbb15de83eb9 -dist/2024-11-27/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=e427d5d6a53abfb53b436df96457d58eedc648198fce6b1d976b50de18cbc31a -dist/2024-11-27/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=11142018c11849c21adaa7096abf50d0c923f3b8f07d67eb96d6860d428e9b27 -dist/2024-11-27/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=46596d8f2271dd5a6f0f84387ac19d0e81976adfc6050b6aab6cc554102fe469 -dist/2024-11-27/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=f3d0c9db1c50a018540278433fed2e0ae4bc798aa0dac87cf0f55ad1808618a1 -dist/2024-11-27/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=c46fc4bf174fc54f5565fa04e80efff4e853642e3e5f0bf5b696a2bd6e7ae817 -dist/2024-11-27/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=706c10ef22cc16b3d4cab8fc09d8783384313b9f774229470b8ae1dd292850b2 -dist/2024-11-27/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=b9cec80ed374f7b5f22f1446e3f2e0dacfaa0e2d4d6c631ad66cc42a23e156f7 -dist/2024-11-27/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=41c67ee95e72a3d10bdf739c4841bd937c4e0b19c33451724c1f603b4bd2a318 -dist/2024-11-27/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=5ad8603f98cff0d5d015c72f3a55730d75d59680a867c067928bf4586c00d805 -dist/2024-11-27/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=61a75701f7e000f86f318a04dc6746f029fb933e0ea94944d76c7eefca1f24b2 -dist/2024-11-27/rust-std-beta-wasm32-unknown-emscripten.tar.gz=89bfbfea23974ed75a53ff134386992e9c257d3e51a55eb49847aec9c5ba6b13 -dist/2024-11-27/rust-std-beta-wasm32-unknown-emscripten.tar.xz=e34d1586bc6767b4b244f98754badda706645fdec7168ddc0a409b9f95f2b18d -dist/2024-11-27/rust-std-beta-wasm32-unknown-unknown.tar.gz=8a88bb923e138d0015c8f46bc1c3d5f2f5f75f40dddfca72a49eed42c080d1db -dist/2024-11-27/rust-std-beta-wasm32-unknown-unknown.tar.xz=79a980f2b999c3e3b04d7e327bd2b548007039b45da705847c6da2bfec3ffc04 -dist/2024-11-27/rust-std-beta-wasm32-wasip1.tar.gz=454e2c4b46b4684e591c6e4c0f1ce8e3c44623a29d561be3a481ae008072dbd5 -dist/2024-11-27/rust-std-beta-wasm32-wasip1.tar.xz=4dcf652ab51fe03c4691de46365218dc483cf1f7c2f5a552a38509685a0ca873 -dist/2024-11-27/rust-std-beta-wasm32-wasip1-threads.tar.gz=c2c34d9b3998633cfa145dc2a5568a99d1fe26b91c5be55a08870a1d27db4af7 -dist/2024-11-27/rust-std-beta-wasm32-wasip1-threads.tar.xz=4d5b25e4b5fb71d879f15f320f7513eec1ad078d3ca6f525ebf56b9620bdb5ad -dist/2024-11-27/rust-std-beta-wasm32-wasip2.tar.gz=e1385a340d3d9a4003cc456156bfbb1435a5255b2bdae3a40b6d7339768bdc81 -dist/2024-11-27/rust-std-beta-wasm32-wasip2.tar.xz=245e7914174d213ddbb550b09fff200f419bcd632de98548a470013891666410 -dist/2024-11-27/rust-std-beta-wasm32v1-none.tar.gz=d5d924ea001f914bf87df8765b013db6e838a930a4865e95f8d35bfaa86bc81e -dist/2024-11-27/rust-std-beta-wasm32v1-none.tar.xz=bb98d0d55f38f2308bf99d4e5f11842683452571075eefa5b4e1f18f2bd553bf -dist/2024-11-27/rust-std-beta-x86_64-apple-darwin.tar.gz=edd5e7ea230a13be750dd74c5f7a7dee20db8683f7978aeb352307a0ce110b9d -dist/2024-11-27/rust-std-beta-x86_64-apple-darwin.tar.xz=c6cec9a501d9873e3c7abdefd5b9418f7250c8cb915be8b816cb48dedf1f201e -dist/2024-11-27/rust-std-beta-x86_64-apple-ios.tar.gz=e970c2a4ebfb69219dc66e4c7541cf22c52da5f94983c7c914df1f93beda3d31 -dist/2024-11-27/rust-std-beta-x86_64-apple-ios.tar.xz=594ae9bf5f1501322d986dc91249a48a61c22371513b872fd63e134097ac55ca -dist/2024-11-27/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=d57bf579b4629417dbe1c02506db4a4ba4428871a2684141d21303e6c99551f5 -dist/2024-11-27/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=714db83e7563a5f286fb37bc629b64834a9ed981bb9a05931122072f95e39bf8 -dist/2024-11-27/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=feafcba9c46ba3e5fa0bd9b5bedc05ef3423c9962dc4d25a7b2367e3e1caa679 -dist/2024-11-27/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=78fbc8f45df64dca21da7223b5d081111922f2268fcc4f1f83595d159d310bce -dist/2024-11-27/rust-std-beta-x86_64-linux-android.tar.gz=74fb57da7f2907afbaa2a7a73847273ef81e2d6902a326f1ae87767677a864d3 -dist/2024-11-27/rust-std-beta-x86_64-linux-android.tar.xz=3216eb73d9c9effcd2a2de7f63d78055c3d7e50fb8141e2910a45a36ce744ffb -dist/2024-11-27/rust-std-beta-x86_64-pc-solaris.tar.gz=3df431c8941ac28906c3a6568d3debe478d9349d868ea8bd3ad79e65792c20e8 -dist/2024-11-27/rust-std-beta-x86_64-pc-solaris.tar.xz=548c2a6104148bf0da21bf5e009b80522fd15539077a62021a64f16282815a5e -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=235228431d67feb4c8e6aad44e20bf68574f335e8f948808b597ae98d827171d -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=5a2d4ed70aed26f902cf45e1dcabc5f25884cf6e9aaff775405ba9c1f0a5cacd -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=818131158e8be6af4b762750f7927c4a799346c94059a6073230da79b4408720 -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=fc08b39833e2c9319dcb55cef5c6d1f0110aea794c9a6f163ca39749a22a94b9 -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=6164c3ade38c03113193301b815c52df0ee84a4ca1c8ef21e1a56d386e28df00 -dist/2024-11-27/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=0846126e6c4cf2b5184979b6025fbb7bddb5c22905bc7ba31fff69f6b1459280 -dist/2024-11-27/rust-std-beta-x86_64-unknown-freebsd.tar.gz=ae308e29be13a176b26c142347a81facf8c11ef1a28e67d5898f4f2a0d3344ca -dist/2024-11-27/rust-std-beta-x86_64-unknown-freebsd.tar.xz=e80bcba8b011a4dd3f7b2a2cea69b5a95ae2cde9b2d9078ac8fdd492ac162139 -dist/2024-11-27/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=b25f0d605b7f4a2a93adb746e32f59bfc26af569a4169e9fb203cb33751d1575 -dist/2024-11-27/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=010f35236062a651b1b54d7c928068efca8dc486856b84b04e98f4f97583bf32 -dist/2024-11-27/rust-std-beta-x86_64-unknown-illumos.tar.gz=db3d258c2caea73b469feb9c11fbd3bcc29d22c3969c406321d11f05c24974ed -dist/2024-11-27/rust-std-beta-x86_64-unknown-illumos.tar.xz=a952b0c2bbed9085525c041c957f00b6b1020e031149dba70c09a1a0d40db3ad -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=817d3d3a931eba2255bd5721435cba7604fe37a72c18a179c1fd5f06dc3b97a1 -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=170a54aa50f161576d4b0d103f2ab6dcbbb81ee57a5a47f7fbd181128f6c17ff -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=ea991dce55415ed7462abbeb1ac1c70795cb628b0460e14230e8cecd41117fca -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=a8f620c3a641f8f4c29d88e4cc54d7637aeb18cf8eaed8571ce558835f742601 -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=875e2e3309b1deccc1a4476e4847a2849df32e0e2a394e4ddf3b4abd1feca534 -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=23f21ac664b3341771109ac69d094518a512391b86219ffec50725dc68a08504 -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=680f5b2c63f58643bd34f9921e2c1699a549410ab06bfd04ce991907e8fd7d8f -dist/2024-11-27/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=dd10f6f3ce39489b6e3f3f638573e850c36518e3b2e8ffad0c50c3143e3a8124 -dist/2024-11-27/rust-std-beta-x86_64-unknown-netbsd.tar.gz=fd31ba490f0bfc53530e34e6e5ab35a52eebee616efc8ed18d473065ea885230 -dist/2024-11-27/rust-std-beta-x86_64-unknown-netbsd.tar.xz=daffc01050f5cf0b5ece91717d7a4c42cd4a148c4c11f641b494d9c375df5177 -dist/2024-11-27/rust-std-beta-x86_64-unknown-none.tar.gz=376d571641a9f1f75fadf951da91bb73ba94a5495d7d33389f5f914ccbec57bb -dist/2024-11-27/rust-std-beta-x86_64-unknown-none.tar.xz=ffd9e826537dc94c6d8e767ef5a61d1fcf0385b8ccbe508ff9177de0fe772939 -dist/2024-11-27/rust-std-beta-x86_64-unknown-redox.tar.gz=447e434b5e40f7882d7adb6d0346629e0e3e92d0f339c7781e5a437898a84b4a -dist/2024-11-27/rust-std-beta-x86_64-unknown-redox.tar.xz=bc21caabcb492ca648e19a8f3b9dc1a1bf7792135a202831deb554ec3ea11077 -dist/2024-11-27/rust-std-beta-x86_64-unknown-uefi.tar.gz=40ca7d9ebcf6610c26f2921c0560729e708f5661bf6167266e9d8cb3987ad0b3 -dist/2024-11-27/rust-std-beta-x86_64-unknown-uefi.tar.xz=06966d5901f5d9accadb47c4d1e561f3f95bd050f3f8e78f30000389f39e9b6c -dist/2024-11-27/cargo-beta-aarch64-apple-darwin.tar.gz=e1a24f081ddd93b483c0f1c196bae676163db50fef0a994bc47c2a7c98c9dfa7 -dist/2024-11-27/cargo-beta-aarch64-apple-darwin.tar.xz=d51a5f0669906eceeee4e09b079828e2ebf3404aaedf1fb20745339205270ee0 -dist/2024-11-27/cargo-beta-aarch64-pc-windows-msvc.tar.gz=e135a5f0f83432b58d9e093c6d560b021a133707e39e1ded90a28a9967105e0f -dist/2024-11-27/cargo-beta-aarch64-pc-windows-msvc.tar.xz=1fbe65015cb8c7ab0b863ffc6f7aa72d3bf52f7830957ede8915c61a22e5b993 -dist/2024-11-27/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=9d4e830e9ba582302865fcefe31883cef4a471e77d46e70838e549ca11a0fbce -dist/2024-11-27/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=233f6347a36e806dcf27ecad4906c90bb3353051f440a819989efcf091cb78b8 -dist/2024-11-27/cargo-beta-aarch64-unknown-linux-musl.tar.gz=6cd719a7255a0eb1f11592b9a92d0aa4986363f624ee0d6077fcc253acab66a8 -dist/2024-11-27/cargo-beta-aarch64-unknown-linux-musl.tar.xz=b874ca8285ad1936adc70a9323cf80f06322b1e6d5925f54b58500129830f0ca -dist/2024-11-27/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=ec7200319224ccabe814cb051e51d147f1ae8c763413002fd03c9263a02c2320 -dist/2024-11-27/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=86e96c80a91862e59123272c80f1cfabdca56bfddcd66af4a5e840af7421c4e1 -dist/2024-11-27/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=3f7cfdcb28fe8c3f0dde58c24a0c424652b40d00a24fc50891351fa5bff5c15e -dist/2024-11-27/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=2d5a184b0103ac943ec4f6756fbc7126773e739e0d120acea7b3244c7cd19b1b -dist/2024-11-27/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=9a77540943f28584d90b4bcae4e234cc9d833eb732f65d7fbd680a0f4e915da4 -dist/2024-11-27/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=7696a065951b7d7179b30ff2fd16a05675b6bd4250f293666b98ec05299113a5 -dist/2024-11-27/cargo-beta-i686-pc-windows-gnu.tar.gz=eaff75076ad71a4291af5465dedf0fb0998dc171e35ab8f707ac63b43e89c38a -dist/2024-11-27/cargo-beta-i686-pc-windows-gnu.tar.xz=205d3d19726a1a87a1533d94f77b73ed9de8181906acb48088dca22835aa7a83 -dist/2024-11-27/cargo-beta-i686-pc-windows-msvc.tar.gz=58da319889533ff7be5c9142c2abdd1ade79de10eb83d7ef82ebd93b75607f8b -dist/2024-11-27/cargo-beta-i686-pc-windows-msvc.tar.xz=5ae699d0f6b9e210a988af5b8f2f0c4d62fb1695ed6fa55718fdf05298039b9c -dist/2024-11-27/cargo-beta-i686-unknown-linux-gnu.tar.gz=362f6338ea929ceb632307031e049dee1b3ed998f5cf160654c327c5e0e7ed39 -dist/2024-11-27/cargo-beta-i686-unknown-linux-gnu.tar.xz=88f14ca7b5c8af006f3c54749b61747485f7e93fde4c65b5969e73503d12c5c2 -dist/2024-11-27/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=9ee8f29e3e715cd7c85e679fd01ca724a27503a50edb0e6f8912f289630bc7a5 -dist/2024-11-27/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=4c643f0d18ba101c2b248e1df32ec3ddba82efc0d5f9b47945d484fd3d462794 -dist/2024-11-27/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=f70952c63e2eebd05acc326cfa667595535fc36db34bc0262fda4ba100ff2145 -dist/2024-11-27/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=0546a908cc12884801c35b461f98db01e87a7f92d276ca6cb6b89eb76c596e2c -dist/2024-11-27/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=3534e839b34603e4e2b32fb8ecc76163ea3b9cd9907c1a22fde4165860d1fb56 -dist/2024-11-27/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=b402a2e73debd81dc5cbc866f00c2ca82f676e2f923193674969af0ab34ad311 -dist/2024-11-27/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=fa69011557fdde5af1f78f33f9ba75a1d6f4488b9d8edd2958582593dba347fe -dist/2024-11-27/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=679be0bf62c7371b92f1824c8718662672ac3ef2d79e8af5c752fe34effbe333 -dist/2024-11-27/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=ecc2ba0da5c1057136eb105ec0727e55396e414d75efe0c2b96bef0477961574 -dist/2024-11-27/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=a89ad44842df861c69c6633ba79bf4ecefc38419d5669b6f22a265dd7be836f2 -dist/2024-11-27/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=051437ff7d177bdeb7a5a0eb947d27b8cf352c8e809b9311a1cc21b785a5a8b5 -dist/2024-11-27/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=1a4f50972104a95c404e778279a4acd42176cae45f58d316d601e7f38dc7bbc0 -dist/2024-11-27/cargo-beta-s390x-unknown-linux-gnu.tar.gz=bbe9812fe890b900e71aa4714dabd2f3e67bdc610fe0bbd5aea645ee807dd0ff -dist/2024-11-27/cargo-beta-s390x-unknown-linux-gnu.tar.xz=eebac7e8555f44ea9094750b61bd11d758197a72d4d436e1713e4bca42fa8080 -dist/2024-11-27/cargo-beta-x86_64-apple-darwin.tar.gz=6773b94f012a3d3589e2bca28f35dd23208fb245f107e4a17b8b3e7f893c038a -dist/2024-11-27/cargo-beta-x86_64-apple-darwin.tar.xz=d3a20dbd60545b0162dc06499317e342bea077cff369e053eb89e85e2b640188 -dist/2024-11-27/cargo-beta-x86_64-pc-windows-gnu.tar.gz=39e16c97c0b22533dfa674d6e8f5bf5df256e2b426d02dca5ed33e246f18d05b -dist/2024-11-27/cargo-beta-x86_64-pc-windows-gnu.tar.xz=ffd939f901c64a17c3f24fc69744cd2af1c176a45d8e5fa8209067ada403db7d -dist/2024-11-27/cargo-beta-x86_64-pc-windows-msvc.tar.gz=8addc12e74a983f3166b89d1d53ce87c92f38c7c3a62ec7c66580aa424d20516 -dist/2024-11-27/cargo-beta-x86_64-pc-windows-msvc.tar.xz=8e5622917395ff50e53b237e7322f1faf2b64f0446c41ccaa0096e57a3e7de69 -dist/2024-11-27/cargo-beta-x86_64-unknown-freebsd.tar.gz=2734d099aa1ada79346213aec8cc7ebe40312df1dfb40a32d09ccd3b6622e025 -dist/2024-11-27/cargo-beta-x86_64-unknown-freebsd.tar.xz=642edcaf16dd74c0935d28ce9b37cf1c5ecb9c03bec30f6ff0e7476760ae7ca2 -dist/2024-11-27/cargo-beta-x86_64-unknown-illumos.tar.gz=4f28de38005de48db7126dcb698b748063341de41dc71175605301837a3f7336 -dist/2024-11-27/cargo-beta-x86_64-unknown-illumos.tar.xz=ce18692f99780ec09d01816277ee4e71785bdfbd7abb4375ba8d87c1b6905ffa -dist/2024-11-27/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=f1d692c3a1c280f34de167ef26af3c13a6d0a3b5ae4fb86b0a9f2178c3287a94 -dist/2024-11-27/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=21f477af2b49ec8809bf8bc7b4c4919c7d2ac57782b829f5a0d9ef1967c5d71a -dist/2024-11-27/cargo-beta-x86_64-unknown-linux-musl.tar.gz=4bed61be221c1347b1649f54222b27e2a3577b84816ce1099c9f0f461c6e9e5a -dist/2024-11-27/cargo-beta-x86_64-unknown-linux-musl.tar.xz=33f463209d50ca6b04a8953c87d2177a84d1729d635de9a7c9b1a711ccb5d751 -dist/2024-11-27/cargo-beta-x86_64-unknown-netbsd.tar.gz=ce46b5b2d767cc0d790d8fd21052ecfb787dffb4218a238a339fe5f1afd26d6f -dist/2024-11-27/cargo-beta-x86_64-unknown-netbsd.tar.xz=0afd0489cb4231edc5bc1ef24a43c3f1918f681f08c1207d184fefdb46416509 -dist/2024-11-27/clippy-beta-aarch64-apple-darwin.tar.gz=6e5f1a85dad2b899567369aceac0102943ab807dad7e218c273b7b4d6393dba7 -dist/2024-11-27/clippy-beta-aarch64-apple-darwin.tar.xz=f784ac4a6ab180675396adf8256ac2bf2d1e823bef106d1c6c59911a8360692c -dist/2024-11-27/clippy-beta-aarch64-pc-windows-msvc.tar.gz=22f1e9299a24aebaf9a32471ddc6cdbf856b455d059ca077170aecf5989f8772 -dist/2024-11-27/clippy-beta-aarch64-pc-windows-msvc.tar.xz=7f3fd047ed5aa85ea6ca2b0d6103f7530d7d070c93b0f212c9d1a6491f160cbc -dist/2024-11-27/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=f383ecb5cd654268f00be1f577572993c38e7386a1e1d18b864338e87c7b2b42 -dist/2024-11-27/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=b2043facf587f98a8f10a92ae129b7f608297709f4ac61d683b7c813cc65e41e -dist/2024-11-27/clippy-beta-aarch64-unknown-linux-musl.tar.gz=6678d45195bd2a7b27ed2d4bad3790e6b63e376da71d112b9333760ea375c40f -dist/2024-11-27/clippy-beta-aarch64-unknown-linux-musl.tar.xz=f6c77e80c9ea4e312fc2715a9f66ea9a2e6344e3f04ab96cc572e4c0e8c915db -dist/2024-11-27/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=be8c0dfde3f09bf476d6600b530aa498b01415015665429bebed8814c7568b73 -dist/2024-11-27/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=4315f7623dcf0a278cccb6c8b397a29f83f70ecb9b03588f61e383e5ebaa697d -dist/2024-11-27/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=c31ff694c68f28e0f462133e783a5d732cf9c539737fc8dbbfd01a53e9a456c3 -dist/2024-11-27/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=2553ae5596b8273d8a61ec66d2e20d0c9733c9f791ca5c56217c60884f0ccf9a -dist/2024-11-27/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=1842cf2c26ab474df47e74720c56997e270b37609f840ac50ab0b05064cb38fb -dist/2024-11-27/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=e882b1cc2814e3c97c33991fa0a80de62556ce8f4f88307f6338a90be32b045b -dist/2024-11-27/clippy-beta-i686-pc-windows-gnu.tar.gz=7c6e08b841f21e3b8953e51a6a9fd9df0c719e8defd7b1ab0660d8920fe72185 -dist/2024-11-27/clippy-beta-i686-pc-windows-gnu.tar.xz=137aec194877446fec994a233427db419bb4f2910151c456c3ec80d9329628e7 -dist/2024-11-27/clippy-beta-i686-pc-windows-msvc.tar.gz=52ba38277ac3f80d124fdc8dd1709b8c1115cecfc53fae4731da8615a63f78cb -dist/2024-11-27/clippy-beta-i686-pc-windows-msvc.tar.xz=548d3eb599334ab98791b8ba27decff8b0de98dfcffadc994ad38aad6255cff5 -dist/2024-11-27/clippy-beta-i686-unknown-linux-gnu.tar.gz=448071394d5e647e8da8f64cfbf85e8305d85ff9357dd24737523c95eb339ab3 -dist/2024-11-27/clippy-beta-i686-unknown-linux-gnu.tar.xz=e25daa6264cf5e0a7cbcfd827e27a8e276c29c88c86cc15977c1a1650d0303f9 -dist/2024-11-27/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=9f080cc937e7209ad83fdb8bf4b2cd7b080af83dbd9061229f154785e273bfec -dist/2024-11-27/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=7d537f66195c581ff51ce8f53c4687a762daf225c7f0ce76393953ab04b804e1 -dist/2024-11-27/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=2bd38261a9ef33ad687b3edcd33f283c32a493a80b56dbefc822c1116edb1f2f -dist/2024-11-27/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=7e63dff1d8abe517aefeb4b1976932d23a9a7ed5e4db2398d8ba2659301f639c -dist/2024-11-27/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=1495b2a49cfce014da72beb691e6c477d189ef34080acb1761b588d11b75d27a -dist/2024-11-27/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=75da94f1ade2ef48674b77ded595c69ee715f62f9d9dd96e72039e27807b7314 -dist/2024-11-27/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=2d6b826fb4d3d9b61ecd3aaffdd1334f766dd5a5fb0d8279e0de26d381c5cbdc -dist/2024-11-27/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=f5ceaca2651d6002f232f71809458ad912bb5f00030790e907ff7b0e12c9e0c1 -dist/2024-11-27/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=1a62f307197c6fd08fbeabef89f58842cfd59c6d62f530850592fe55852d61f7 -dist/2024-11-27/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=4c65fd6a5d74527391fbc7c242a2313e5290209f5a15763d74eeaa6cba0a0ed0 -dist/2024-11-27/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=8ab9f735a98de1949e65d51c3050b29ceba3ddc28167ba0180353253dc726392 -dist/2024-11-27/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=0367eb23a9349ebd829d59fe0a2638557c8e57205e48f66a701b238461385bf2 -dist/2024-11-27/clippy-beta-s390x-unknown-linux-gnu.tar.gz=ffe9100ec6b81b081d5f9d840bdf01c658790d7e6bc0674e5ebb6b28f79c2f7f -dist/2024-11-27/clippy-beta-s390x-unknown-linux-gnu.tar.xz=b19b1b28bb7ddf8cf977f102acb623f65d5117c44ab8b62cf197f2d3d33c15de -dist/2024-11-27/clippy-beta-x86_64-apple-darwin.tar.gz=d986de5c01bde3469693dea7cb4248155ee9791550aa179d601066d27e45afb1 -dist/2024-11-27/clippy-beta-x86_64-apple-darwin.tar.xz=8000e46ccc2598fdb770c69c68aeb18d94db9b2847d0509f7a9c51ef28714ba7 -dist/2024-11-27/clippy-beta-x86_64-pc-windows-gnu.tar.gz=c701581f28d885889ae5c845a7b34beebb04a4631c2740e9333b69390cfaf499 -dist/2024-11-27/clippy-beta-x86_64-pc-windows-gnu.tar.xz=fc285792e6f626e133edede613a9a9a90ee12191765e2f0beac642a3b3c9ca12 -dist/2024-11-27/clippy-beta-x86_64-pc-windows-msvc.tar.gz=91afdb71bdd54e5c04fc7c933dac06d5e769627e886e20eb712524e852a01966 -dist/2024-11-27/clippy-beta-x86_64-pc-windows-msvc.tar.xz=2ef7018c7319e3a299c5eddf36748d9d293ae43eaf54662d9855fd211eb4658c -dist/2024-11-27/clippy-beta-x86_64-unknown-freebsd.tar.gz=02234d4c47478112e01d51bc7dd48cd467293e1eeba55bd383d08bc7376c471d -dist/2024-11-27/clippy-beta-x86_64-unknown-freebsd.tar.xz=a3e6a0c7f31278866d97816c5ed434b1987e0bc5a89b24283e836402803c1388 -dist/2024-11-27/clippy-beta-x86_64-unknown-illumos.tar.gz=b238ab536ac963929884da4dc9bab43679e6fa9aba4251c73ab7e5cbe5d8eeb9 -dist/2024-11-27/clippy-beta-x86_64-unknown-illumos.tar.xz=797ea7a7643a8302e45347f21c4dc6bdee617c2a9d9ccef74ed525c7d2e9dd91 -dist/2024-11-27/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=d630a6b25a08af85d3d426e73d3231e018e0ab7176a5934572786147fbbf3823 -dist/2024-11-27/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=d0259a0112f452ee49b8c9bebd760ab8945e5bc3a0d57b1cdc2b8e24accd35c7 -dist/2024-11-27/clippy-beta-x86_64-unknown-linux-musl.tar.gz=5febe478e5040f0f74a14c23e78eed21b080900d875d6b447354945c07677a6f -dist/2024-11-27/clippy-beta-x86_64-unknown-linux-musl.tar.xz=552c35bbe76a83439e66cf9ccd522d97e43a8fe4f6aadbabd1f02c4b2c1814dd -dist/2024-11-27/clippy-beta-x86_64-unknown-netbsd.tar.gz=ff6db2d7b84e1b01c81e494de15ccd07f60fef6dc60fa46d47e128ebaba4022c -dist/2024-11-27/clippy-beta-x86_64-unknown-netbsd.tar.xz=b27f89b175bee3769dab1c2d0c54f17ff3efba2038f3b600f4077435c7fd21d3 -dist/2024-11-27/rustfmt-nightly-aarch64-apple-darwin.tar.gz=5b5015483d0a98e9dc715c3cc8c23598c02fc14ea6e5878790ae68f2f1c8ef46 -dist/2024-11-27/rustfmt-nightly-aarch64-apple-darwin.tar.xz=ce5706f0397a1b2fd9e17dbf34ccfbc3e8c87cc81c92b54b81fd33accc2b7c06 -dist/2024-11-27/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=3f5077c8743b5c4f233da7d0aa0aa13fc488ef0fa9da24b002bfa413d52dc845 -dist/2024-11-27/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=087eb30f24ba7d8ee42e28bce2a6cd75e5fb7ef5dca8a738ef23fac82ad59566 -dist/2024-11-27/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=28a716ef9c5318543559ab4c0c2d062b0deeab1ecec80d5d83ad5effb574f209 -dist/2024-11-27/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=99cf9ec4b7bea4281f64a613784350a8ac34e9b0387fed7eb26d6f8c21c83735 -dist/2024-11-27/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=a23c3de89603bd4d1035a267e6ee456c6de12efa4029d76ded3edf7c69285a15 -dist/2024-11-27/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=462dfd28b631b5a6d8277920cfa8037d8583cbf6a2e5c9159c492161013e820a -dist/2024-11-27/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=44af5b13726cab40865580df8bc05182a2359eae64e41a92f84790ef55ca6bdb -dist/2024-11-27/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=0a7c0b207e745d01194d96f0fbe2402273d630d4f1aa05001a5f5371c9585a2c -dist/2024-11-27/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=66e2fc688b8f746d15c0dce042f2a94e06c5b652d5e0b6534d9e992eec186784 -dist/2024-11-27/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=15a2e087f7b482fa8c027fe6e6fbaf846d784c8e917fed375048a297d5e13c45 -dist/2024-11-27/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=71788ea2544f8349ec548a2f150dca2afe80a49deb91c00c46484a5e3039742d -dist/2024-11-27/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=d9fa7b780b9d8b93549abf0a3f0e6f31cc8e1e991ddf682adf3b616fe2a1f0c8 -dist/2024-11-27/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=30924bad51ffa62f762d4c000f86ea3457b590f6397d65755c711ff7f77ac129 -dist/2024-11-27/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=14fff13942e6a65927776c49013249b2e75aa3a630667ecf49c9ba721e47bcb4 -dist/2024-11-27/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=02d82688bdf3bd3c261a4a6d4acfb07835e29ce262cdc3165ba849a6a8490bcc -dist/2024-11-27/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=2a19b55bb609542ec49dea87e3b67532e5d18df8be43d0ad775bb34f4f9f96a0 -dist/2024-11-27/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=d07ca0ddfb0dd89a9cc935b2b9662379bcc9503cb28a28d68c5165f787a7d762 -dist/2024-11-27/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=de8106801b1e49dfd8a4fffbfc2a4f949218eaa011ca2085953ebf2a5ea9b141 -dist/2024-11-27/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=309024f86e80a4568721b5bb61aa936e027accc251fa97acd5043b513b325576 -dist/2024-11-27/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=55bf234aa2ec7fd4c6a7179f779d790e7f62969967519bacfeae84db5cd29abe -dist/2024-11-27/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=4bf9571182e7ef40e76c7e5905fab8ac35269ace390f5007480b4951f80cfa3b -dist/2024-11-27/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=2a2cde80fcbf68768ba7a99cb059a0cdc9313ad0c934383dde9598d6147ef756 -dist/2024-11-27/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=b747fb16b15cff3f9368ce1a1f5cad0d2930bde5a609547a3afa2679d7ab8a1a -dist/2024-11-27/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=b861f8361ee806d83f38afbbbf312e0e5e4d8851e7048444c409de1a83484446 -dist/2024-11-27/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=ebe56700ad0948e4dcd9930730f7df8e01a5eefca1d1157fe12160d782e2a5c0 -dist/2024-11-27/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=5081c9b8677314464d8a3a50220809def18badb2269d2cd8b7106f3547b0e25a -dist/2024-11-27/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=2ed9094b538b6450371311b742f2d309a1f40b2b4c84deb9867059336b76d1b0 -dist/2024-11-27/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=d9d52d9b9f4da7a457d05dd4645885d092f170bd2f751a49b4ab3b88395a5248 -dist/2024-11-27/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=a2e51d381645c6bf18502ca72fe9f10ffd309281dae87ec16c5627f232f77e50 -dist/2024-11-27/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=c526efc054c33a08626b882eebadf887a9aaf8fd60a4adf6a146659a7c1ec8d7 -dist/2024-11-27/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=04e5b7c97b57ff7d967abb4f98374f0833108c04f6edf482b8b3fefbde340956 -dist/2024-11-27/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=1829afdebce1c3fe249f81958de68c765db18b431927e54fc42be37ea4ab8226 -dist/2024-11-27/rustfmt-nightly-x86_64-apple-darwin.tar.gz=d6cf138d02e50168f8ee4a89abb2a4a39e19458d060d7b46cf227bba1fd4c0d8 -dist/2024-11-27/rustfmt-nightly-x86_64-apple-darwin.tar.xz=e8185262c82c064c4bb9ae6f1d3072819410de9cfca943b77605012a546c254e -dist/2024-11-27/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=64d78f9f05a978b95b9e22a63bbb31dcf98152df5638f498a361a96d9d2b0c04 -dist/2024-11-27/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=c07041ab84364ace58a357c18b5c4dca037852e1159edabb02f4579ac6853b4a -dist/2024-11-27/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=7ef0122ccd3a0c77c917bd75e93358eb95d7e87d78a165c724e3f0cd90f8209c -dist/2024-11-27/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=d0d0566a0a50e1e9e7f4251cf207bde82d96c27016f0a0acc364754561ab4881 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=e4854528c31316894339bfa97b07ec607e8956877c285778e555885ce9c8a068 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=d79c9b352ed7b2f132f06dcdf360527502b2153beb09cca968a5ced521edcd39 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=df76cbeae0a61506a29db22e8743d16fcd97ef2da216ea15bf4d6cd709814017 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=103cbb30bbe92da6666d84fab53dd7fe8105c2ebe62eeab5f6609488e62a481b -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=5f3c61663a319d6162240192f387e10ab87e2a972062198082158a243b667d7f -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=221b45c3928b1c45fedbeea987ad80750d3f55fbc564cf3ccf910a68447ad725 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=18eb24fd665ce1cc4ce66b37b19c22397bff9369963b127137780870c179228d -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=bcd09587a21ea10953a14521e9e0ba7b5100e8e15b6a10cc4e7bd359200d5279 -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=db712c5e1d21231770e12dc95f6bcbe2f1d04b9cde61bc7adf8b529b41773adf -dist/2024-11-27/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=43303e426752dcd15964416822cdce0e5f3012366a9b77f3d68e6011c7bacd0f -dist/2024-11-27/rustc-nightly-aarch64-apple-darwin.tar.gz=95d543a835b11cb5ccbf0578df1ce4e1846e4915622d23a36cc6e18e44e17f29 -dist/2024-11-27/rustc-nightly-aarch64-apple-darwin.tar.xz=db9e0c51c35c81c2ec8be0838f3231f7374917215792ffee159e3d7ffed855d8 -dist/2024-11-27/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=bf9b83d6418bf97caf2df70a8e8298cd8fecb7b950cdaaa2cecbec243ed57c74 -dist/2024-11-27/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=bb520da5b522fffb5a20eed4390990c2ab9d22eb4f77196535a84ae7a434c9f5 -dist/2024-11-27/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=4072d9ca2d617d09d394139c0b8608bb8b2b8b2b4fa450f13c41975a16a791c7 -dist/2024-11-27/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=3d75a8a534e86bb55b63f5b5b2df66ae47862cb7f3ecd1591a829435031d7279 -dist/2024-11-27/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=3cf05e90bc1f95e92cca4769f74055245234037009cf464df3062238c88bc2b6 -dist/2024-11-27/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=245ebb7886cfba64d667630ca8bd672520cfece0ccec3b916e3de0f26aeada52 -dist/2024-11-27/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=dadff4abd2f169f9aa1910d4ea247b7d1f767fca74238ad31485b73399ab6dda -dist/2024-11-27/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=ff97e1201e9a305673e8a9b740b7696cc4cb5e86bfaa3e137cd8e3e28cffd7a6 -dist/2024-11-27/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=0c411e0824140323f51600d387d52bd01f601f5539d6457815e950224a6d29a4 -dist/2024-11-27/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=dc5731a70c393f69555433d522cde8a52893bfb88beec519c1bbb03b2a0e060c -dist/2024-11-27/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=01ef2f07f1222fbf3e0cfc5b957c287fb886fd0bd08cbf18f90dc37fb0b63541 -dist/2024-11-27/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=d45042b3ea0c0ce6befea7bfdd731e890dbd1f47d1992fe8718a1363f9f18779 -dist/2024-11-27/rustc-nightly-i686-pc-windows-gnu.tar.gz=5437da4c0b73d203789829512d856a66c1697d95afb1ffeaa6347ad0f7c04672 -dist/2024-11-27/rustc-nightly-i686-pc-windows-gnu.tar.xz=a7cb4cb627d75de1ade9211d8ea489ada9ac76ed38909751861ef0cb729dcbcd -dist/2024-11-27/rustc-nightly-i686-pc-windows-msvc.tar.gz=fd6ee6fc171459092e5e8a012d9cfb7491313e021d1c38965605836b5b101b0a -dist/2024-11-27/rustc-nightly-i686-pc-windows-msvc.tar.xz=314c7cd558a3654db85d75b3ed71da7cfec3614e4a5f2c449dbc6663b64c7e3d -dist/2024-11-27/rustc-nightly-i686-unknown-linux-gnu.tar.gz=7a1118011b11befb7df2df57e4450c7611bb3503b3b3cfece9c9c7d4a304391d -dist/2024-11-27/rustc-nightly-i686-unknown-linux-gnu.tar.xz=2cb95208e77546bcce56d8d5646c3fb5e49ed711894926cb50903ba10431a36e -dist/2024-11-27/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=4a76c4493e5ba19aaf14947a6c2e28ebfc7f2da530f1441a9fdfa328e15ea4cf -dist/2024-11-27/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=9b361904c691e17fbf44d80c7f7789511db1d8f251d65ba9cf6fda7f06fd495f -dist/2024-11-27/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=e89dc76ab061ae23081aee1619fcbf4a94e3acefef6b9b149231f3e1c22f9ec1 -dist/2024-11-27/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=11266d557e6d1d9155e6f04f65e4815cfbfd9f8e1aaa47221030e3ff11b22f78 -dist/2024-11-27/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=30f2eae1b8694607a04c836f750b4b7c204e1e14e52ec37885b9a821c2c9646e -dist/2024-11-27/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=495316e70b26140c66b074e9d7f714ab949f422c2635cab784a5e133538e4bb9 -dist/2024-11-27/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=ca560c4fe28051573d54f512652ac9740a2d1111aeb8e36b004a6ff9c325925c -dist/2024-11-27/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=1191f15bb6da98b01b43f0e9d7f51c5c45d38e3c5be2b4ae5f7c0c8fd25e9a90 -dist/2024-11-27/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=5f85829aaab919fa4b2fa5ac843b87358e8da4797adbb6eeaed5be02666ce964 -dist/2024-11-27/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=7c671d366fec561f0b568bfb4b6a08a6071303077a60f76da1307453e51ebc36 -dist/2024-11-27/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=300307ab6cf88afc4312edaa510769e901598492deec4834176c6fc9f3eef6cb -dist/2024-11-27/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=6fcf91c8679f4963f7d510cc8afbc24a1070b25ea6d20d5d31ad78ea659da194 -dist/2024-11-27/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=ab905f6f3119648c83a2f37ebe1a55b7899a5ac6d10070e4dbbfb89762a369af -dist/2024-11-27/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=13688b6857c8b10f7fb497f07c969d406cb6b76869957b5138f4b20fcc5d7c5a -dist/2024-11-27/rustc-nightly-x86_64-apple-darwin.tar.gz=e6bb782014e34fafdc5dcf3aeb407eb636d822d346c5a8c6227cc45bc645561a -dist/2024-11-27/rustc-nightly-x86_64-apple-darwin.tar.xz=6016ae620deddc42d1d6b6134b0164dab45a204089754b73db41ec837aa88a84 -dist/2024-11-27/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=ed3a381858612e25cacfd89c08496e9d06378d593ccedea4217ad4fa1ab326d4 -dist/2024-11-27/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=4819bb9c25aa743de7ab7e93b10b30336a87f1f75e5122d578df2c83fbc59850 -dist/2024-11-27/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=404aac87c77871c80f83cd7c59390af9af704ee468f40eba68d224d23e2c84e9 -dist/2024-11-27/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=0e1e0d2454ad24fe1da2cce2dce425bc1167bbca68832da2476c7996c00ba612 -dist/2024-11-27/rustc-nightly-x86_64-unknown-freebsd.tar.gz=946d5bcc3ac0b83fd0b53b60290a6361b179f16ba2aa6a2467789ad520278792 -dist/2024-11-27/rustc-nightly-x86_64-unknown-freebsd.tar.xz=f3bd1a053399ec89dccaa7456da41d54a8e91deb5c628e4c502fdcf34c5fe780 -dist/2024-11-27/rustc-nightly-x86_64-unknown-illumos.tar.gz=c353588a38705b7ae050d4b9868b6c4d3527295edbfb25c115b22cda4912be1f -dist/2024-11-27/rustc-nightly-x86_64-unknown-illumos.tar.xz=a71d8b2b42f6cd522da237f99492ed59cd34ea8c86fc1e73c8854c8234e1c980 -dist/2024-11-27/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=5116b949572fd6c7b11a079d9453b15a4c48562bce22b26d82b7050da772789b -dist/2024-11-27/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=3635df40363273d44c6f8182f5f6a3a099ed55897bb764cab2565363cd31c1f4 -dist/2024-11-27/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=5233af0b907fb7b176d2b5fee07c486e92dcbbbab1d54a60d814a827befaa984 -dist/2024-11-27/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=c908cad6d28657f1fdcbe9d84ecb86f247c75134803dd5273ca24c6c68a58f37 -dist/2024-11-27/rustc-nightly-x86_64-unknown-netbsd.tar.gz=c799165c319218a2fb4cdc0ac8db87fc9e2992a8e402d984ea27938a5ad1c5af -dist/2024-11-27/rustc-nightly-x86_64-unknown-netbsd.tar.xz=ec12763fa5d4cc150f663361cd245c30835e05e3b1928898b1b7300de559468c \ No newline at end of file +dist/2025-01-08/rustc-beta-aarch64-apple-darwin.tar.gz=8869795c998e9d39a7093bf53917291b066e35964059298cb1cc2ee2f1088b2e +dist/2025-01-08/rustc-beta-aarch64-apple-darwin.tar.xz=1397b1ee764dd51d8a082a063b6b13a2d5609b1e1a6e1dbbc0368f24e7830e85 +dist/2025-01-08/rustc-beta-aarch64-pc-windows-msvc.tar.gz=68719c1f92c6aa4251c27910c9f788472166444a9295cfa0af6450bc6b5e6555 +dist/2025-01-08/rustc-beta-aarch64-pc-windows-msvc.tar.xz=56fca9013ab1646263f043898346dfe206f4b264c37d1cf39fc61807e47c072d +dist/2025-01-08/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=7acc2b71f759f098c542f0ff4f3705e73112dfb3b8e85bb9158d1ce10d858efe +dist/2025-01-08/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=0d21e19b284d2053879ee55353f0d601e3b359530cfb9c747f6cc4ad9b67ade9 +dist/2025-01-08/rustc-beta-aarch64-unknown-linux-musl.tar.gz=25a5b2cf16fb51056c208e5899c9843bc3a902ae2f4148ac53f0b9043727e321 +dist/2025-01-08/rustc-beta-aarch64-unknown-linux-musl.tar.xz=31b1dead28d88897fcb2ccdaf72021faba9febe50d797e70ff5c29ae82179618 +dist/2025-01-08/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=fa8d3c93890425c9a0d130a652d3a4ecd018cb3098efe243a3ce8422539cb9ce +dist/2025-01-08/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=15344286d365042d9bd8cced1268eed508aaa6152d5e3e289fbd5ebc87788e75 +dist/2025-01-08/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=c5ed0dd8c2a3b609d9fdd88b027a1d628517107af8b5488a74bce9a3a4dbebcb +dist/2025-01-08/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=ff5eb5bc42dcfedaae86bf31af37c6c2bdb6210e98cbdd780c4e1a63ba37eaeb +dist/2025-01-08/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=6108711c53f07920a9afd0e749ff4228396220ec2eeacab2310e5b32af7a5e91 +dist/2025-01-08/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=7bd41663ab83aa4bef55f3c44e4e4cd3c4291c967573cf8b2ba3d60eda9d06f7 +dist/2025-01-08/rustc-beta-i686-pc-windows-gnu.tar.gz=12ba512d2dece4f9be9ac08f3270bfa6663aa28a3443f1cbd7f852ab052f12ab +dist/2025-01-08/rustc-beta-i686-pc-windows-gnu.tar.xz=834cd8f4962401dc522a5a0ac866561a62c646614a6ce4b99e91619098538861 +dist/2025-01-08/rustc-beta-i686-pc-windows-msvc.tar.gz=9915fde0d52fcc41eb5f5eb19c06be447187aaf3e5b4f438f7c20d92b364527e +dist/2025-01-08/rustc-beta-i686-pc-windows-msvc.tar.xz=e4e79679d6f90b5de5c64a534ff150416e48aed276d7426a1d4b1fc2e517cefe +dist/2025-01-08/rustc-beta-i686-unknown-linux-gnu.tar.gz=e394e8f0361f0f8a8a9653cab3ade57de10622651675e27c137cef2b06114a6d +dist/2025-01-08/rustc-beta-i686-unknown-linux-gnu.tar.xz=756965d724f5389a3cb63374b4287ea48e18d38827ac3af61a127fc6561ab865 +dist/2025-01-08/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=4e3483a31356c4c7c0a7041d318b36e02bcd25ac25c396e4dd139ff4ecad5af5 +dist/2025-01-08/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=9fcc0531f285832f5333762317ff64dd2506bc263e447f1931d361b15430d0f8 +dist/2025-01-08/rustc-beta-loongarch64-unknown-linux-musl.tar.gz=780ab392bd8d879efc593ec9f87c1ff927e750fea4fdaae23d327fc363f8078f +dist/2025-01-08/rustc-beta-loongarch64-unknown-linux-musl.tar.xz=bfad2585877df4fc99fe708187336631140a8170d21159a6deb7cd8f8ed20248 +dist/2025-01-08/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=30659e0526394f50ada52ce228cbe342e7ad9f6db2f0e1dfe6c8c63eb7ec3dd4 +dist/2025-01-08/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=e59cbdad42098b5f402f612c957113e7f7b8f4b4f37292079187f44d1753360e +dist/2025-01-08/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=adc19d38f3927cd2c09e2b4bc5e31b1b2473d45027b839abab1837ef22b6cb31 +dist/2025-01-08/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=35b15e40ea3b382d67e98dabbb93916abecb7d18977652760e081c1a112300d5 +dist/2025-01-08/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=31477a7164f8b3658bdf192bcb1383bb77f93e684887fc7b06e55bb469e7e25a +dist/2025-01-08/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=67f4e6f541e16eafa55e803e2c41fef1c892e7408f61228bdaabfb3a4cb41ade +dist/2025-01-08/rustc-beta-powerpc64le-unknown-linux-musl.tar.gz=2f4b8b12fc8bd7bd5783cc77e9b798de3ecd4a17f5a09d6fd4f3a528e1a243a5 +dist/2025-01-08/rustc-beta-powerpc64le-unknown-linux-musl.tar.xz=474f1a2b2f3a2379da02ba24c59a160a7ab0a2bdb4290b0b277d8aea8e441a53 +dist/2025-01-08/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=6be8f141e0a3f00abade77cde91405ac107d729f24b946c89831baa674c210d9 +dist/2025-01-08/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=897be1e29a2b67a9dc2d69195ec1f41439903dd3ad8a7b57eb339344ca6ec5da +dist/2025-01-08/rustc-beta-s390x-unknown-linux-gnu.tar.gz=b58ebbaeb5474f577329f4d48991222621d4f30f6411a00973951917586e2cf6 +dist/2025-01-08/rustc-beta-s390x-unknown-linux-gnu.tar.xz=215384ed943e173fcb4e10baa69d6929c99e8eeb4790365f1067bf20d5417b32 +dist/2025-01-08/rustc-beta-x86_64-apple-darwin.tar.gz=40a3d42489eb77cc7912af5580632f8f6f27373b0f691d24b7d249ca9bc84a41 +dist/2025-01-08/rustc-beta-x86_64-apple-darwin.tar.xz=6f8c83b96ba2d9bdb623af0f09b6227532d8c0c0f6ac19fb4b0c5f5145d2ee9c +dist/2025-01-08/rustc-beta-x86_64-pc-windows-gnu.tar.gz=1f76fb03cca2d0fe466c995467f209c28a68d8ad69e7b2fce521f4ace94106f8 +dist/2025-01-08/rustc-beta-x86_64-pc-windows-gnu.tar.xz=5bed9303cb18b7c5d78875b11463b945c9c9583c5b377efb69462bda0f92b190 +dist/2025-01-08/rustc-beta-x86_64-pc-windows-msvc.tar.gz=a64fedbd98a9d2e8ca6d21018abf627367e2c81a028d38fb850253a6c94ef45b +dist/2025-01-08/rustc-beta-x86_64-pc-windows-msvc.tar.xz=471c040fe9f119e3ddf74fb8887d03cecc415109577a85c14917c00ab3348d0c +dist/2025-01-08/rustc-beta-x86_64-unknown-freebsd.tar.gz=468f89e4f6b7e72cfeeb19eeb723e723673487f3d1cf333d860623bf454e4ff9 +dist/2025-01-08/rustc-beta-x86_64-unknown-freebsd.tar.xz=c9e699c5fdbf0af0434354caf8745796db42a931669e733b3a7432baabf81974 +dist/2025-01-08/rustc-beta-x86_64-unknown-illumos.tar.gz=e0dbd7d05f74a1d444efb849700f98bbe6b00f870036e5cf6f065fc2d0b982b7 +dist/2025-01-08/rustc-beta-x86_64-unknown-illumos.tar.xz=1936046fd441ed9bf8331f77ac9ba930640523ded0674831912d28f9f7ae90c1 +dist/2025-01-08/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=c6dbeb9017e7f9694ddb05c4658250945e1d6c2b6de656306a3686d499653029 +dist/2025-01-08/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=adec507e847d746ef1bdb0464b23ef7fd840d17e7bff787a198fae239b69a77d +dist/2025-01-08/rustc-beta-x86_64-unknown-linux-musl.tar.gz=32714ed814213cc64fb1721c919c43a877286f625289bf5d526e247de0941e05 +dist/2025-01-08/rustc-beta-x86_64-unknown-linux-musl.tar.xz=fdebe84ac7370ca3cfa66cff6974c00e506aab6fb3ecb6b70e8e2c3ef92025f3 +dist/2025-01-08/rustc-beta-x86_64-unknown-netbsd.tar.gz=6443e8f6c2cd4d5f4a0b7f38e1c741061f42c454d006afc06ab87968425a8f44 +dist/2025-01-08/rustc-beta-x86_64-unknown-netbsd.tar.xz=c12851a0437c7ba6edf08d9ec89f2ccd22f05a5de661664142bf4f15d2a658a2 +dist/2025-01-08/rust-std-beta-aarch64-apple-darwin.tar.gz=e88734bd578a36ca396032144b25717e18ff51c519ff9490b193117516b299be +dist/2025-01-08/rust-std-beta-aarch64-apple-darwin.tar.xz=60f5672d8759ccf632e072cce922dd9d079a8e2325098b278afc4fa829943a2f +dist/2025-01-08/rust-std-beta-aarch64-apple-ios.tar.gz=bfd63a92d85035bdd4b4d445427c68483a34b41f647b09390de2ac9342ad6b54 +dist/2025-01-08/rust-std-beta-aarch64-apple-ios.tar.xz=9e9b941612bb29e317e4dd10ced4cdaec502526fa964b3aa29cd4fc2af070451 +dist/2025-01-08/rust-std-beta-aarch64-apple-ios-macabi.tar.gz=2dc9ed6bbb9fd1d576bc78321a78f7997928a6a94625d90a613286e1a5bec758 +dist/2025-01-08/rust-std-beta-aarch64-apple-ios-macabi.tar.xz=ecb686e8f201952f1e3475a88de73eb264a11c74961967bd1ba7eee87a248bfb +dist/2025-01-08/rust-std-beta-aarch64-apple-ios-sim.tar.gz=b481a6f88b8696b86160541083e608ef811f1ec0d9b1455325fd48efdaca6f01 +dist/2025-01-08/rust-std-beta-aarch64-apple-ios-sim.tar.xz=584d7a1cd7be4af3c747269de3466a312e541dfa17b1614ad355b32b74ea1ad8 +dist/2025-01-08/rust-std-beta-aarch64-linux-android.tar.gz=c91990c99c5687216320bb82e9e2f8f7b2242fafd534cbb0ae88a287880c2dab +dist/2025-01-08/rust-std-beta-aarch64-linux-android.tar.xz=c3a4a168393d0a8222b8ebdb6b5495a8896788156af4c454e35d349d1ab63109 +dist/2025-01-08/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=5a7c5b256b41a9ee01387da8ffaa49a3914737c827113beba081f34671568e84 +dist/2025-01-08/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=d0343608f884db1579847539eb6346ad3cb7b85072bf698598db61dee720793d +dist/2025-01-08/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=6cf78705518f865a7c6d3c9c4566b0dca6197338d5d88dc0037c721261e5dc3c +dist/2025-01-08/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=c8c81ffc20538dea3050a7b7ba89cab620423e66a10931befba354e2c4572ea1 +dist/2025-01-08/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=966f512ab3d23e303f7162d03bc619aff54b0af25bab86bedc0c96e130c89545 +dist/2025-01-08/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=fba9f964d8fb51753435c300319870a3b4530e385887a16d31975597cfde3ca7 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=997d422eedd7b6adfd175ef6a13b5ae7a83530d633f834e7ca0d94851ed29ed6 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=5ff49e69e9b549494542c9306c1fc82f6221b8d9e9fe25ec5021fe46f7c8cb28 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=2181081deaa27438f420df53847b8108f96ad215369cae81947f0688b1906cc2 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=f8b28aeeae9973c1dddbea684eec9a6a136fda3f850b4ffaa10e53914e1037e0 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=a49eeccef00269063a10b1e623a54b0b500afbc2dd8e1db4ff8ae4a6844de943 +dist/2025-01-08/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=32fa668c9d3d703adbf6910ef358dbcab344d37fd3ce4e81ba43a820d8a8792e +dist/2025-01-08/rust-std-beta-aarch64-unknown-none.tar.gz=95c6a6a65ede296e8b4183a1271dd6cb1c61299f3966a8e2a6180867752a2988 +dist/2025-01-08/rust-std-beta-aarch64-unknown-none.tar.xz=a44fa8ccbc73899c013526ca32866f63e1165e7f3b14455b91b37e3865c9e1c0 +dist/2025-01-08/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=31448ceb30ff86b5d8b4e7fa8fe5871665115d561e6377f73e164d8f11652b6e +dist/2025-01-08/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=bd71a90489b9ed6134d0fd05fd22c58b20c271422628a552d04bbc2bfd1bd29e +dist/2025-01-08/rust-std-beta-aarch64-unknown-uefi.tar.gz=0cc22881954c661d2ae01058ca04db10912289812e9cfdbca3c066227e38566a +dist/2025-01-08/rust-std-beta-aarch64-unknown-uefi.tar.xz=439e6cda62ca17f124ed788ed8c3882c2999362067214ed911aa362b8d4d22b8 +dist/2025-01-08/rust-std-beta-arm-linux-androideabi.tar.gz=c62e75f3754b04b3594189de953ca6a8703679b29e8da3935ee5a7370ee0e03c +dist/2025-01-08/rust-std-beta-arm-linux-androideabi.tar.xz=dd23a5df6353aa01271f0d1206f927dc0f532203fbf66258b2771155e543b86f +dist/2025-01-08/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=2cf5fee1c6cc0e2ee099c8687126b11861e12b098dd4ac99397a2ad026596350 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=d3a946d098ca9991b4dbdc4ba3b53e37535e62815f76f81bdf7e4562f3534943 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=2eed53b995d0ac0c2856c39815ea08f40c074165c3814336af591adcd9f8b571 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=3848bae0dfc660d40486ee56589fd0d6ee558f051a14b5efd9d55cd03d10f709 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=b40192daf614574f7ef2f7785f6b66d99c0876f3f3cdc30990f8545d41ba0fba +dist/2025-01-08/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=9e23ebe3062f38791cd23cfa58d9fbd796553bceff7a5a041d3cb78278915c99 +dist/2025-01-08/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=1b47d8f88a4496a8c7604aa2b6b0a2a810266825c385df5e29d313d5635eab6f +dist/2025-01-08/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=613dba102c8e8aed939ce3f1c43382d3d5d74a1baf911b7f68867ce0abaddbf6 +dist/2025-01-08/rust-std-beta-arm64ec-pc-windows-msvc.tar.gz=6afea3865efe25d2675c6e988c09c1fdfc052b27d996f893d9e5fe8a5aee10c3 +dist/2025-01-08/rust-std-beta-arm64ec-pc-windows-msvc.tar.xz=285ce1584fca2d0701baffb3228801c88212c20df1a6ee76bcc6e8a392de8739 +dist/2025-01-08/rust-std-beta-armebv7r-none-eabi.tar.gz=8bee2bdb49b1b8781b016b0b1032663e20d135d23b7d9de898e7dee0732a8ce2 +dist/2025-01-08/rust-std-beta-armebv7r-none-eabi.tar.xz=d0e2b1d1a095bb02866048e6286fcf762cffb69f437031ba3e7f330ec1ddaf07 +dist/2025-01-08/rust-std-beta-armebv7r-none-eabihf.tar.gz=c9a13d7fe85291c560786835a0531a407459f07cbfe4b652d9497a581e4bff09 +dist/2025-01-08/rust-std-beta-armebv7r-none-eabihf.tar.xz=be5e9534e88732ca700315d8a4517de3701201d74a0344e962faa9e9c64cf035 +dist/2025-01-08/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=9a48d8e2247f3f70c85e8a5fd39c9f1302d56aa94fbac6b431bff5a4c74f64ad +dist/2025-01-08/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=5e210bb961e6f73869671298ae1172a3a6f61b1b4ab08baad81e0e32e0a25245 +dist/2025-01-08/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=fc842d5db2778c0f3fff7602e8056795a0c33880a29ba0d29941fec2d2174d8f +dist/2025-01-08/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=cf473390797976ca153701dcd3ee41a4c2ba314cb180a06ee51e242f5f93047f +dist/2025-01-08/rust-std-beta-armv7-linux-androideabi.tar.gz=2cf80485cb303b44d48f6555ca33ea25fde6daa7f5fc9e6a2acdc23697c810a2 +dist/2025-01-08/rust-std-beta-armv7-linux-androideabi.tar.xz=625e57d27f5b4bc198598df7b2ae6ac8416951060eda7dd08ef9d319e97b2b9c +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=170d6999f1f9185d48df161023e527826ad72019c37ee11a4e3ad25fa1667e81 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=724bf705fdfd23aa4065ec9128d6d34beaa8285b02f36c2a68d83b1f537c5186 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=21a6727b7bee42a9ed22167600a5c9e8346db3e10133c88a07acca89a3513140 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=f6234d0030296bb13cd8b075a6b2637b5f8c1c9454ff764a168755686fff4b5c +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=09479a598b1fe96eead29bc72f007a130b374b53d06be22282ad94ef7b4e4bce +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=7e67daa06adad6878ea5d3cd9626baf370188fde3e14b2459347665118173c5f +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=92d63ffb8ef0c274ff6138032e0df849b7d10668035ec3a20304ab31bafad495 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=1c2e84999fb5282b6ed7bb31bac0f4c979483696453f3c797dce2e52b7bb848f +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=3a5e0748561fb981acdcfdc2037b12cdfb51d4fd3336172f6be74a78eeaca6b3 +dist/2025-01-08/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=9bd198313fe8e4d0d3b4f999841ad6310884f6a0ba9f1582e072108e38fbda0c +dist/2025-01-08/rust-std-beta-armv7a-none-eabi.tar.gz=45cdec4bacb8c1cb67e73c26499fa789fffebc981a33472f6b84d8752554e7c9 +dist/2025-01-08/rust-std-beta-armv7a-none-eabi.tar.xz=5222471cb625865c8a2594e88a3adbf529a866164e98f77a315953d5e3494c54 +dist/2025-01-08/rust-std-beta-armv7r-none-eabi.tar.gz=32159e59a8c11c032ab900f77ab7b25827409be390c62bfee8e28d9cd733c13d +dist/2025-01-08/rust-std-beta-armv7r-none-eabi.tar.xz=45c7ba589c6f092ada1a0acc0a107efb09b3baf0549846919ac3f853b574026e +dist/2025-01-08/rust-std-beta-armv7r-none-eabihf.tar.gz=4263a0a290f88e9b5c09c6140aaa02fcc7bc455439070f6d4b4d9a38f8424c3f +dist/2025-01-08/rust-std-beta-armv7r-none-eabihf.tar.xz=a3ff2f3691f7f62f9bb57259eb4e6c2976276fabc46e9ac6a7796ce2fd6ece5c +dist/2025-01-08/rust-std-beta-i586-pc-windows-msvc.tar.gz=d073f07548e5a2d92e094c69b6a7b59cf91309ebb42e06393c410c95fa90b681 +dist/2025-01-08/rust-std-beta-i586-pc-windows-msvc.tar.xz=e8708e2a341358c14c3259a8c4c05db6eb7baf09050cdab383b44f0b70dea7b7 +dist/2025-01-08/rust-std-beta-i586-unknown-linux-gnu.tar.gz=9072d9bf10db42db4e150b73fddf1085495979a18f59cabd43d73c0f1336e112 +dist/2025-01-08/rust-std-beta-i586-unknown-linux-gnu.tar.xz=e43a369c7954f4645243990291c88dae3b6694e4d2efe7ed4f81b7b8b7c723c8 +dist/2025-01-08/rust-std-beta-i586-unknown-linux-musl.tar.gz=e7de687f826c37e20b64b5e091fc3ea514770d38c45bb39eb6ab5a9730be6d11 +dist/2025-01-08/rust-std-beta-i586-unknown-linux-musl.tar.xz=5ab4c606c30f534fa75351160af4c4dda660955658a0d6864d28d927b9ca07cd +dist/2025-01-08/rust-std-beta-i686-linux-android.tar.gz=5936848fc7f45feeb797c6a78889762d0b36fe48dd82eb437049be6e8d17ee04 +dist/2025-01-08/rust-std-beta-i686-linux-android.tar.xz=d66a746f476ad27525c9f22136b664c0a98a811450caa083ff8c9def80dd886f +dist/2025-01-08/rust-std-beta-i686-pc-windows-gnu.tar.gz=d6326c139001357e964fcad7e26b195fb707e8158791fdfbcd16910258438910 +dist/2025-01-08/rust-std-beta-i686-pc-windows-gnu.tar.xz=f455268ea0f33118607adb1a7ebe484a38904e1c698899f4857492633fad219f +dist/2025-01-08/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=714846d27f13247f37edc266b66eb1792fa1c35d2dd13215800db55ed89cf8c3 +dist/2025-01-08/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=cede0b7977ee57ed24d792749dbfb42240f95a235d93b2e1a7ad4aa03d588da3 +dist/2025-01-08/rust-std-beta-i686-pc-windows-msvc.tar.gz=70fbea3fa87e1aa93668f2462ddf73b05c55883ae9d2bbdd0a3acc9ab51eb961 +dist/2025-01-08/rust-std-beta-i686-pc-windows-msvc.tar.xz=bc13da70c924372a2247d125bdfc850b9551e32ef990ca8dd0c24d282f9c7025 +dist/2025-01-08/rust-std-beta-i686-unknown-freebsd.tar.gz=c4583d916fdf07e7e274b59c32fe661b0c1780b925c4d84d01fccb1262cee70a +dist/2025-01-08/rust-std-beta-i686-unknown-freebsd.tar.xz=77612c690f5ecbadb1f2d9128f79676bc3b199478cdb619321eb521e4f8777ca +dist/2025-01-08/rust-std-beta-i686-unknown-linux-gnu.tar.gz=c487e6e164f31a9274a24a1668170db22e64def6412a38a38b6e0c4741523786 +dist/2025-01-08/rust-std-beta-i686-unknown-linux-gnu.tar.xz=cfd25efcec9004d223fb7257a34c0530ff755eb3337fe80468949df185e1adec +dist/2025-01-08/rust-std-beta-i686-unknown-linux-musl.tar.gz=5e208e822d9d2db30bfa4ca363d29d923c5560ac7d220dfd32898524022aa2c7 +dist/2025-01-08/rust-std-beta-i686-unknown-linux-musl.tar.xz=742f8c4dde07be899de43502af87ea1457a4706078ea7f353914fd31d9b91537 +dist/2025-01-08/rust-std-beta-i686-unknown-uefi.tar.gz=3567303608260def6590e9f0c29ce776ce933f0a9cf4c568faa8713935ee9efc +dist/2025-01-08/rust-std-beta-i686-unknown-uefi.tar.xz=dbea586494aeec9d2d29c80eb259cb6d66608c93b26f560f2276b490d6946a25 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=ecbacd29dc59c59ac513958325ce3a2a2323156562dbf966f529f267fc52ce17 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=69c2429bc0bdcb3e4ab9b05af37f65c60b936df6598d375843e63b8141371fe3 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-linux-musl.tar.gz=f4f33e673f883a9ac3b0113281c890839498d70d72e5ba51094c851bd6f0d323 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-linux-musl.tar.xz=c82917544801fe373b007aae871b4833db428a37d544e09644601d3fde682018 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-none.tar.gz=d3fb635af8745de8a5019bb9bbc3b20b109ed85f7ced6e939836bc0a124a0fbd +dist/2025-01-08/rust-std-beta-loongarch64-unknown-none.tar.xz=6e7e2d66e1c99507b1b515178d6a200532e1ae7600dc125d26b8ccf52d011399 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=0fb8285f64d616d741a32ae07f69dfa96e0d231cb835a839dc42ea76fdf0ea83 +dist/2025-01-08/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=c1c92ca9c3153ea5f42aeec0c3ee7f1042e3e01d6a91784e6baaf201c9621608 +dist/2025-01-08/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=1749eb869811470620cf5e432b5f5dc128c6b8e9f3b9586fc20336c677b863da +dist/2025-01-08/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=72c7a3c88afab27d0e4b09c313af336e6aff45046fae2becb17235affc1746f6 +dist/2025-01-08/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=36bd0cb97b921cdb915337ec1c7a23609c94c2485f932a1c27fd71804def533d +dist/2025-01-08/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=45a6098b27a0de50e3c26d540ab4afeac30178bdd389c0a4d829b015cc5726ec +dist/2025-01-08/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=00352c0a8fba406add478aa1bbf6b6ca99e7c5db302901616bd2cc740a9867b8 +dist/2025-01-08/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=69d7ca4226590875ae59a45b45e7726780dcf4091c9c44c3759931e2b05a42af +dist/2025-01-08/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=6243b4efcf2435cf586248ebc9b99379e8b86ce5575d8845821a11c21f8946fd +dist/2025-01-08/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=78898b9d99a25075a8c47e7b78e35b64b891179a8f54d41983370814f33b6eca +dist/2025-01-08/rust-std-beta-powerpc64le-unknown-linux-musl.tar.gz=ab293f2e7e3288a4c4842ee510307f18c9df6618702695add978bd1d56be21b1 +dist/2025-01-08/rust-std-beta-powerpc64le-unknown-linux-musl.tar.xz=57b7c326e4167d01385f9f89d89e81b5b660a7e63e1c17b14b70e8c8e50774dd +dist/2025-01-08/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=167fca173e6e2974a35be4c8fc5cc9043a3b34d74efba13a86df75d2600576d5 +dist/2025-01-08/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=4197393d32094f65fc781bf39ba95fb36988508f9ab6b41300ebe2ebad838640 +dist/2025-01-08/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=d3d0569bffb348bc580f3e382557939a4a87452a05dcfe1fbca8c8a9888c457d +dist/2025-01-08/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=c0ad9def2f73a60b674b35b8f53483a0c017ad4e4db3b268b3c10bdbedc13625 +dist/2025-01-08/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=7f41fade9abdf4ee47e96ffcf345fc37231d60a645ce814b19738689018b77cd +dist/2025-01-08/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=d6f21bf60530c691a6d40d0ba1ebe0ee041bf8a333f489671b1f1fea61950ffc +dist/2025-01-08/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=0ddc2427ea4e448789b8053f111f7310f6da95d6cff8f644ce1f3ccffeb16f42 +dist/2025-01-08/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=77a2f8531ec6a87ef95ed5690c31d5e1e3cf65a633e0ff885cb0b2cc55a2fab4 +dist/2025-01-08/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=c72a5c97347dbf43c328170cd1ded9363f1f3659732d9497d73bc2a9e3b32de7 +dist/2025-01-08/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=584e99c8fdfd6e186509ad41f67d02ad92cd9d887c2b6159ad8a36334eba7aae +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=536f1135a697180ed7674b8d9ac15232eac4d7735cbcc836cc099cd6136c39b2 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=dad89574543674fd72dd2a6be4b2753b4e2211238d6208b201903c0645024fa5 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-linux-musl.tar.gz=1fb747dc33e3016ec5f0d8161dadef20f0798f7a65967c316f0315826bf7e8d0 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-linux-musl.tar.xz=05c57bfecf04cfb5279a3132138c9bf0c6773ea5fa01315bdcc83f0ad06165c5 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=83e04e9c3ca61c2aa39a0c37b92d11a8571792244e6c5ebc795c1c3274a9a926 +dist/2025-01-08/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=4c1537651b695764db9ae3c536a605a7a9a1fdf51689350f7fff9638454844d3 +dist/2025-01-08/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=1c3164278134db89dc7cc5d2169402f28757e55c99438cb86a90cbf06f07ef7b +dist/2025-01-08/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=ddb7ffbad2e308d80b901986f6e00f38ecc59359a9f7d28223c089b01ae86f91 +dist/2025-01-08/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=935d864e0e212ed9f70027fa7f6afe18757e561347c4d8b97221db2936a5afe7 +dist/2025-01-08/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=18cc0e5b000474a60938521e23c5ef057e184616ddb1e2b031db0f8b5d87931b +dist/2025-01-08/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=e824c24bb3933941658a3a6c981e469c153aefc5bdb153216d90cbe8dc26adf8 +dist/2025-01-08/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=d40178695aff134127268c21a91ae8b4aaedf2d78122a00c844e0c4efe246815 +dist/2025-01-08/rust-std-beta-sparcv9-sun-solaris.tar.gz=c37a91e3c6685a11bc96acb1f7db48c30a45ed19c9a7f303ad4b60bb24228975 +dist/2025-01-08/rust-std-beta-sparcv9-sun-solaris.tar.xz=cf190f95859df6eb65ca3cc07aee95d4f3f8c0770c480ecd8373168cf0bce323 +dist/2025-01-08/rust-std-beta-thumbv6m-none-eabi.tar.gz=b598d8aba2ac570320ae7a1c8b9103f04cd0b81df37500ed838b88874eb7317c +dist/2025-01-08/rust-std-beta-thumbv6m-none-eabi.tar.xz=ebd4c1ca68d4ca5944a3f0837a8a1018e18dcb31ef8436a4d02c347b251ff0be +dist/2025-01-08/rust-std-beta-thumbv7em-none-eabi.tar.gz=3d0385fdb184a66a1af91e3db28ea0829b1a498f2431a896aeccf2c900cb9624 +dist/2025-01-08/rust-std-beta-thumbv7em-none-eabi.tar.xz=f778bfbb7033a53c61c3af187ee35e9e7713e9e98a6994f11ed9b589f3459391 +dist/2025-01-08/rust-std-beta-thumbv7em-none-eabihf.tar.gz=0a1669fc57856b2e475a9b8edd7575ad7ab07ad0b95a0ba9fb92e7fb26f81a0b +dist/2025-01-08/rust-std-beta-thumbv7em-none-eabihf.tar.xz=bb6b245b183061f65e3a5d9e9c1bafc76c3f968ed7d0cbd0e33ff878f5e30804 +dist/2025-01-08/rust-std-beta-thumbv7m-none-eabi.tar.gz=3f459b3a6dbe99fa6bcce86e853096902f23e7e49a1b8c300ef4515796619b28 +dist/2025-01-08/rust-std-beta-thumbv7m-none-eabi.tar.xz=b9ce85bf7cfcb13526c2cbf56aba726919b818168c6f99fb855cf00ef8d0c76c +dist/2025-01-08/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=366797feb0b31dccd500310903b494682b654800938a79c46e47313accaf622a +dist/2025-01-08/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=33c50d91b1f23770031a3e1c9b3a78f901b30831bbed8c7f463c29ebb86d5a91 +dist/2025-01-08/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=43acc164b41da62e1aeb7f252f6d81f1f3b79281362348817134760185648071 +dist/2025-01-08/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=c93bc0681027e879719417049e61ddf7f4092d72d103d14994520bccaf6ede19 +dist/2025-01-08/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=0b36bef54f41302c871bfb92f45f248c34bd2450b159c68801eb13373d464e69 +dist/2025-01-08/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=7f9300e17e4786747ca434c1f1a0451aa7c7f933f7952b5010a0e4bf4d9e4198 +dist/2025-01-08/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=ca421607c9f67d0648dc7550781bf90983dca6f17e81153ebcc4ae2f69fe88a5 +dist/2025-01-08/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=0b20c32941914bacc1b0a16b334cd32feed1ff56e6a2551aa0ae6b22c41f4c46 +dist/2025-01-08/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=e792938012865b5c1fcec63bc25a9b72e28889515472cfd83d7d57fd8f84d7c5 +dist/2025-01-08/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=15338fd8f01e0fb422336ba055a4ccf4be3ff8ccdb123ace84e9dc9f1a64e3cc +dist/2025-01-08/rust-std-beta-wasm32-unknown-emscripten.tar.gz=c57a71f14db1b122dec0ff70a04faf1b8a7a3af948e3ed81b15563b19c376334 +dist/2025-01-08/rust-std-beta-wasm32-unknown-emscripten.tar.xz=dbbd2251d206de84de81abd5b29e4ed1b08c00155141e08a7b6fa35559ac4e65 +dist/2025-01-08/rust-std-beta-wasm32-unknown-unknown.tar.gz=852381932ef65b7ce75f943164ac747bcce3b1d3f6e2c1cd434934c1030f789a +dist/2025-01-08/rust-std-beta-wasm32-unknown-unknown.tar.xz=cbdf58a9afb016aee98416924578d0a8ece8a6d5a17734606928bedf37719129 +dist/2025-01-08/rust-std-beta-wasm32-wasip1.tar.gz=6dd459e217ea3b4f7f85811eca500c774a36dd849addd9874e4e54e972fc7c7c +dist/2025-01-08/rust-std-beta-wasm32-wasip1.tar.xz=beca27838a50a2da0341d1bee58faaab18d67a41cd1724d39b30b4f8daf18359 +dist/2025-01-08/rust-std-beta-wasm32-wasip1-threads.tar.gz=8a264acc2656f35e61d4ebf8773dd3b29c0e7748ccaf326898089838128c609d +dist/2025-01-08/rust-std-beta-wasm32-wasip1-threads.tar.xz=eac3340c629af165b67982326cbff6cd63a2721e41972b70826a9911af902e02 +dist/2025-01-08/rust-std-beta-wasm32-wasip2.tar.gz=ddef01fac3d8b1116b0f2b5f7b4a65a54070e06ccff8fc0429ed8e668d57d4fe +dist/2025-01-08/rust-std-beta-wasm32-wasip2.tar.xz=6de7754db16d34a556264c6d37ed66480e832aacf2aefe34a7e82636ccd38653 +dist/2025-01-08/rust-std-beta-wasm32v1-none.tar.gz=8f8eef02573a4911630ceb59534bb91a084ecc9d8ba1ccfa6e59370f69af028f +dist/2025-01-08/rust-std-beta-wasm32v1-none.tar.xz=86bb30d39659171af2120a86668f1803f3c1ca7b2a0f8662fc98a499734f9e17 +dist/2025-01-08/rust-std-beta-x86_64-apple-darwin.tar.gz=b4e018a5dec5b232867770a8d8ce43d670265a51b389c7a1f0f1652a9e2b685e +dist/2025-01-08/rust-std-beta-x86_64-apple-darwin.tar.xz=b81c60d6b6c5f5fb1eba3037c545452bf7f070eae7b6c23ab3011f50de1f01ce +dist/2025-01-08/rust-std-beta-x86_64-apple-ios.tar.gz=61a88f5fd29b413ae351db90a43773ad2cc846ef3a03b5747e98bcab7865b988 +dist/2025-01-08/rust-std-beta-x86_64-apple-ios.tar.xz=977ad619301f051e241f898c302bc230ed37dcd6041d925378cba6811a15f41a +dist/2025-01-08/rust-std-beta-x86_64-apple-ios-macabi.tar.gz=184c6fb1f051685f23a04ee2a57d15b8f1f3d5696b9cee0f8d48b230f108f408 +dist/2025-01-08/rust-std-beta-x86_64-apple-ios-macabi.tar.xz=90715fa792f08da57964abae2b8366337c3372ecb92624783448ee6fc921792e +dist/2025-01-08/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=13343f16a12d12bf5dea13e240a88e50855a3eaa16d87842e33356611ef6a0dc +dist/2025-01-08/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=f273923ea18276e3440003bfe6f00479df907dffd18fb80b447c41baae396cb7 +dist/2025-01-08/rust-std-beta-x86_64-linux-android.tar.gz=3c86701e32a065bd9b0529360379b86180724497b5167f776253abba208db741 +dist/2025-01-08/rust-std-beta-x86_64-linux-android.tar.xz=daf93cde1b13402a2e7ef7faa2aa1644cacb2fafb211a28d4e1c7cb96e738372 +dist/2025-01-08/rust-std-beta-x86_64-pc-solaris.tar.gz=5cf8d53338a4c504dfb96dcbef5b2862921624c2c93edc7597478f97ef3f1a24 +dist/2025-01-08/rust-std-beta-x86_64-pc-solaris.tar.xz=a8a4105596906d4b1ac1588c1b301c1843c4ea695219fa6a979205a6b5fb336f +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=f9c03150ce779c7a31ccf5fa4e12953af8d182bd7c0415300ea0ddee191a0e47 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=fb81f14fc43c8c9e86da1f10dbff6474447029781b716c9ad67068f793f07ba5 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=de329a0e32d0f66d50da01875c85ac807b826d2d2efbf971a7b46f89710dd2f7 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=ea5ad2523161e086b0b898c8c989fc1b20a26b460f4403eeaa9fde881be58465 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=b042f515a81b7151a0930e852fcb23632b74579ddd8be53dc37bf09fa94747f4 +dist/2025-01-08/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=6f850810bcb1ab381e0245ce7d7881d86852d041d679d622dab4f4317aca54db +dist/2025-01-08/rust-std-beta-x86_64-unknown-freebsd.tar.gz=e415f16fbe3bfd0d9283ee41dc385f3a1a79f33730aba9d04da2acf7ea993617 +dist/2025-01-08/rust-std-beta-x86_64-unknown-freebsd.tar.xz=ce6967397e9dcf20c700957d5ef3a8fbe35795f0b7be1e8f2fc61ebebdd9771d +dist/2025-01-08/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=6233e4e853dcbfe5dab0b1258fdadeec39f3d343fd99e597956677a949de1945 +dist/2025-01-08/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=228a0ccf377f633b450b6bdaec99f351cc9db41e61d3caeba85f675bc9e07954 +dist/2025-01-08/rust-std-beta-x86_64-unknown-illumos.tar.gz=d729b6e0f6d70104c730e7cb4d4472ce02452acceecabf98e411b2ad0d6cfdfc +dist/2025-01-08/rust-std-beta-x86_64-unknown-illumos.tar.xz=51dad4bf611ab3bef919a9961f1a4d02f0203c63db501e32ff0810b2a1d81617 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=6c859df64c707c7783863a17e2980c2139a98293473363efa6873d6549b52e3f +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=f4cce8d15f242168ba0ec7942a1140b1dadcaeebc8c0e7e7bd35ef7f91e4b390 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=f7a559f89b5fa340c2974b8c85aad21e48995af01add8e459b82bae890f0b2e5 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=73007a7a396864a71003de912ceb847e8684c4c63d45f032da628267aa28cb7f +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=7746927bd5283c05717e006d252bba797b48bb9f50b3674587025125e1916ff7 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=4d2cc7aaa081dcc0b9fbf09601881023b6749d8b2986a01f82caae0218d2cdc9 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=6f2ecafe9003c8fba1f43e1acdd477cf0482e7f97c1687d6255c696bc45cf662 +dist/2025-01-08/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=c82ece042d0980b1707d3871aa35b4b816179c3214e84ca7666e2189e4f062cc +dist/2025-01-08/rust-std-beta-x86_64-unknown-netbsd.tar.gz=d41dcec02cd3dc2536786aa461256bf5ce1b65174ab6cc532564dbfb6dbb37bb +dist/2025-01-08/rust-std-beta-x86_64-unknown-netbsd.tar.xz=cfc7dd1eaf1420db8b87d13bd9a857e258c2db4137ce1fab03583b82cff5e3b0 +dist/2025-01-08/rust-std-beta-x86_64-unknown-none.tar.gz=234209f51b8e8637cb2742cd482be0a333e4463869abbd5f00e548f03eb79483 +dist/2025-01-08/rust-std-beta-x86_64-unknown-none.tar.xz=36a5a189997d1c6eb05397007223611fc9777cee6fc495a885e46c2f52f62040 +dist/2025-01-08/rust-std-beta-x86_64-unknown-redox.tar.gz=a8df20db51fd121a970d6dcd24e583ac831c956f37b7e47b1533cba6f4b27dd4 +dist/2025-01-08/rust-std-beta-x86_64-unknown-redox.tar.xz=d1368bfa84bae9366cde19cb201fd13fe09d2c0b53f82cabefa85a78acf423e2 +dist/2025-01-08/rust-std-beta-x86_64-unknown-uefi.tar.gz=625834a177f20b99f706b9b25c60388db6cc42de7dd54a9ae10b16a86f4ab9bd +dist/2025-01-08/rust-std-beta-x86_64-unknown-uefi.tar.xz=89a95f2c6a11a0788fcb4fb12c7b37dcae62ff682ae8e1d24910f64dcf801be6 +dist/2025-01-08/cargo-beta-aarch64-apple-darwin.tar.gz=be3bbcf4c95c1d30634cdd87efaf0ea57b8199649a9e502245ee3915f93f49c5 +dist/2025-01-08/cargo-beta-aarch64-apple-darwin.tar.xz=74eb6c112313e7bc0104e211397ef998c2713f3c350a0b8995355ea04f4c1df6 +dist/2025-01-08/cargo-beta-aarch64-pc-windows-msvc.tar.gz=089863e43456e8c2613cf63f82172c9b2b220d582d448a88a9301f12aa5471e2 +dist/2025-01-08/cargo-beta-aarch64-pc-windows-msvc.tar.xz=fe604aaf5d859192fa13c78643c072d49b31f1b16fdbb088e2917631aa683431 +dist/2025-01-08/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=f4c710be8d286696c57bce005e43458a5031fec2b706a8e825c498e7b1c9faa1 +dist/2025-01-08/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=18e510f07b0a6b94cc1ec14503c7a2c992ce17c9897c472d9330cf55514ca4c2 +dist/2025-01-08/cargo-beta-aarch64-unknown-linux-musl.tar.gz=b2a4e280c9150683d14ab98770a4bb6f27c74144abcb0d075e81e537cbd2e2d0 +dist/2025-01-08/cargo-beta-aarch64-unknown-linux-musl.tar.xz=096d0926b219a8dbb6952a66ff742936c7307baa236262b2d6ee5238c2b44153 +dist/2025-01-08/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=58eb3f62b876ee4be22ca51720204da9c3734a5a4a5365dedcede0a012a48891 +dist/2025-01-08/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=1ca32ac5479c6d27191c7726b0dad38af34e6423ba8e644461ca30c82382ce06 +dist/2025-01-08/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=45895027f54b7c4f1222196317ac1cc38a84ccf07696a72fb91b2d42218d4281 +dist/2025-01-08/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=f08639a9a86802637b08df47815e2b0e32ce826f76c7187a140f97abf1eaebf5 +dist/2025-01-08/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=63dfd33bb41426e81a8be487580656699502396350cd6c50a3bf32cacf2f5eb6 +dist/2025-01-08/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=1033972fe7345a4cb84d0ba8a479159bdda53d42f581a7087eb517144026e870 +dist/2025-01-08/cargo-beta-i686-pc-windows-gnu.tar.gz=2f00a1e75d995f89ad2b5fd509dcbce97aad2096638cea05d0e9662fe8f21997 +dist/2025-01-08/cargo-beta-i686-pc-windows-gnu.tar.xz=535e4ea02ffcb59ab5c492689aa25aaa7dd026a9db7e5a5e1a674464488e86fb +dist/2025-01-08/cargo-beta-i686-pc-windows-msvc.tar.gz=e2c01590495c6c2384afb0287104bb9e9d8ba555c100c18c8bfd7f06bb4daf43 +dist/2025-01-08/cargo-beta-i686-pc-windows-msvc.tar.xz=6ace1bb6fb5cc03358433476d31f25076eb8d963d685df9c8dfc389a9caad479 +dist/2025-01-08/cargo-beta-i686-unknown-linux-gnu.tar.gz=a50b87367e59dcc3a1c22101d69cbf45a1485e42b2e12b6211d0f35059698d36 +dist/2025-01-08/cargo-beta-i686-unknown-linux-gnu.tar.xz=76d25cafff7f67abe142ba7183b9e1a1fcc18bbfe6969ea03783fd1cbdc7cd68 +dist/2025-01-08/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=3bd98bbaf3c1f718e4f40aea4dc0ad47e6c33b46b4b169841497b49a582b7caa +dist/2025-01-08/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=62e384bb07173661c6c1e6ddad3d0fab31a25184966020f70b8492c916cf852b +dist/2025-01-08/cargo-beta-loongarch64-unknown-linux-musl.tar.gz=6bd1ed4631ae5020d4529fc404ef4cf6bf4a93616fd15e1cad9527277fbe200f +dist/2025-01-08/cargo-beta-loongarch64-unknown-linux-musl.tar.xz=e27425b6e8f6c5114aedce100068bda25be975d80761d6478c49588c45f3611a +dist/2025-01-08/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=2b14ae8e9f383d2682292ce26bb5dbe9dab69b450564a5e3f7db3fb93864a21c +dist/2025-01-08/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=dcb68fdece4a4a365a95c440de9bd360f8c1079f4f15eee3d61a53d83957c7e3 +dist/2025-01-08/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=9e9c94abd8fcdea831451105bc351bd9407f3ab4925226a171cb0d170a4a697b +dist/2025-01-08/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=4cdd8795488a4c6ffcf9bc446576191dcc364e58dee785bf3ac2e756e3a9d5fa +dist/2025-01-08/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=4eb0a9d2a1b85326c393250873a4550c8e6617e56df9a2d313aa220ffcb7a9ca +dist/2025-01-08/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=2cf68732cc6926ae9727f627e64fab12c9fbe8803a640b25697ff6af23dbd8ab +dist/2025-01-08/cargo-beta-powerpc64le-unknown-linux-musl.tar.gz=4f276075cf0ce993ac88728992369d996dcbe4e1f68e5e80e620c0a6b305a484 +dist/2025-01-08/cargo-beta-powerpc64le-unknown-linux-musl.tar.xz=5b52acb5500d93fdccc9bdc03e6e91313deff0a2801c2b8a587681239b2a6ecf +dist/2025-01-08/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=8325fda118d5da0b376c0db751cf2ceeae2dd3021b930e3b55c14e02d0ac438b +dist/2025-01-08/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=2a764695907dcef6b4259831636d0020e72dde5d213f532b3f04b0bae3615d4f +dist/2025-01-08/cargo-beta-s390x-unknown-linux-gnu.tar.gz=12045986bfc2c346a914bd0b18d84d3e1c3103d6082b08659aa2508a483e0817 +dist/2025-01-08/cargo-beta-s390x-unknown-linux-gnu.tar.xz=ed09f5e3c37582c4ab42f0b4ef1353ff29182579818be28d8a184b33bec728f2 +dist/2025-01-08/cargo-beta-x86_64-apple-darwin.tar.gz=d026df870421796b76749529fd0a7965d83c2d915fa42599613c7521f92e391f +dist/2025-01-08/cargo-beta-x86_64-apple-darwin.tar.xz=b2174c6efe4336f76d30f55cf83dd5604b2a4f706a76be1cc57780a45d5fe1dd +dist/2025-01-08/cargo-beta-x86_64-pc-windows-gnu.tar.gz=7047bc8731c2eb00043f0237a12f5261942a1b6b99d574cbb6db32f2f1930180 +dist/2025-01-08/cargo-beta-x86_64-pc-windows-gnu.tar.xz=578f2d6d433dc249fe923edd0519f3db953c0a9beb8a2f2641e1bdb65863526b +dist/2025-01-08/cargo-beta-x86_64-pc-windows-msvc.tar.gz=0b4dadaece478e1c0cadbb479bd2acffda3254f2ca3c2529e3b81a0357019239 +dist/2025-01-08/cargo-beta-x86_64-pc-windows-msvc.tar.xz=19dc7572f4ae2a9b46c9017f78c5ff7108e9e293f21a3a52eba03f29dd204f62 +dist/2025-01-08/cargo-beta-x86_64-unknown-freebsd.tar.gz=71672eeeedc18240a33ea8b0ee0ce2ca8fdb020d5701cf4b8d3cc464a0199cdd +dist/2025-01-08/cargo-beta-x86_64-unknown-freebsd.tar.xz=e853f0e42602ff80f64fe245863ba0a4f50d8197ab4cde56c4bed53f4d7d9cbb +dist/2025-01-08/cargo-beta-x86_64-unknown-illumos.tar.gz=42c1b82bfbccc05f381376b59c92d453810ea57d85e206b9ad958d0e9faa480d +dist/2025-01-08/cargo-beta-x86_64-unknown-illumos.tar.xz=b83b7cfb56b087a0dc648eab5f695104a1f220896a2981d2b65f67aa1584838b +dist/2025-01-08/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=0b87cfd9dd3349b23fdcedad9a0fca316ac59e720c4c8f7b67c44020dcaa9665 +dist/2025-01-08/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=19883a7bde6ed9a4587469f0c7bacb434b9734eb74c4b9c570fd3c217db6a52b +dist/2025-01-08/cargo-beta-x86_64-unknown-linux-musl.tar.gz=11934bf3879a9c395676b9fb737fc9e6e0b54314e51008875a72a80d8e773a74 +dist/2025-01-08/cargo-beta-x86_64-unknown-linux-musl.tar.xz=2ccf643a305060f2fcd24b957a8f9db28dc6fbe44d1b947007a571cea976d973 +dist/2025-01-08/cargo-beta-x86_64-unknown-netbsd.tar.gz=f9982255fb124e74a79708e9e566fd3713b904919260ea498279cde580c9b899 +dist/2025-01-08/cargo-beta-x86_64-unknown-netbsd.tar.xz=18bd7da3c593a0bdcb14140ce19e5a43d42e041fb897fa4acf12f35e03b2d766 +dist/2025-01-08/clippy-beta-aarch64-apple-darwin.tar.gz=511e0a0b6bf59a8e9ff299a32e46586ced5f13a1ad43c9564921c27b905457c3 +dist/2025-01-08/clippy-beta-aarch64-apple-darwin.tar.xz=dd75a377e5a28aff5eca49c73b0b52a2141f347fbb3d2c164d87122663c60ed7 +dist/2025-01-08/clippy-beta-aarch64-pc-windows-msvc.tar.gz=3097ed66faa8489f2fac32453d2ebeb7bc39d5d58b2fa60d47e5f539a57d3658 +dist/2025-01-08/clippy-beta-aarch64-pc-windows-msvc.tar.xz=dd26d47db7f35a41eb16829b35079d2137eb41a254c810e8a9e6d393a899ed96 +dist/2025-01-08/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=a3bdd2413e3d040a1063113d6ba868ed4ffad157f97f180ce6410dc07d89fec4 +dist/2025-01-08/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=308402abb63ae6e364d4ef014aa9dcf338b86982a25fc0570c4ca0e348b50bbe +dist/2025-01-08/clippy-beta-aarch64-unknown-linux-musl.tar.gz=07a3f0f0f6eca797d9feb6b3339a7c87ac4063bd4c898cb49d3a61c43b7b8ee6 +dist/2025-01-08/clippy-beta-aarch64-unknown-linux-musl.tar.xz=9b2fbae0ec014d61a3d20e532b526fb92bde03ab154b0ee02cb5aa4071c319cb +dist/2025-01-08/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=9d6c781e2414d9ba43648fa958b2cee3937fa62d118ffbd7ae11fd71ca797011 +dist/2025-01-08/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=73d6d532acdcd6c64f48d12a24ee4abb6f46c6dbfc3d5c3d593887e98313c1a5 +dist/2025-01-08/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=0d698e42477ce7c068df67b389e8f63f82649e1e2b38324e3c3b8906ee9f056d +dist/2025-01-08/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=9b94df3a1bcf891595a8263a61e3aa82a66013a5c59e8d8ac7742e033a1ac590 +dist/2025-01-08/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=c010c51bd0665e4fc8a3d0bc27af75d2bd112dc8cbb841252b3d44b3789665f8 +dist/2025-01-08/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=c7f9ecb195ded5ac7a03d353e51c264e5ccb4a3460733b06fdf967d8e3a5f085 +dist/2025-01-08/clippy-beta-i686-pc-windows-gnu.tar.gz=5f4ccf0aaeff0eb0970796ea23aae4590285b42b62b165fe3898649e203f57da +dist/2025-01-08/clippy-beta-i686-pc-windows-gnu.tar.xz=4400d650806cbdb2a8e8e2d6ed0bd5f90fa46b8f2398f09fed98ef565135635f +dist/2025-01-08/clippy-beta-i686-pc-windows-msvc.tar.gz=062eb313a72787fe53435c57b8a5b0c16ad2abd0644d8669038090d3572bc94d +dist/2025-01-08/clippy-beta-i686-pc-windows-msvc.tar.xz=ae493ceb9e334b05345cd94a26b87f317909ee82c02f704c2c3b487f5b28e897 +dist/2025-01-08/clippy-beta-i686-unknown-linux-gnu.tar.gz=0d7ba60792aa6e5cd531362ab92977ae3368b2ca2284174019133c2ed9d495a7 +dist/2025-01-08/clippy-beta-i686-unknown-linux-gnu.tar.xz=ded4113933755736c3248f1b0e26cc42d436eb4c6cbb21745f87a2428eae6ae9 +dist/2025-01-08/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=a4525272c4d46ab120d61855d5e453b92b0fa83623c8c5cdf4384b746ba56925 +dist/2025-01-08/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=5acf5b947a7d7f546cc405975ce5eb1f7e1a86ffaa6ea7c18f5baccbe8e26a7c +dist/2025-01-08/clippy-beta-loongarch64-unknown-linux-musl.tar.gz=628a86af3590cf6ec46173bfd18790958618e1236d3bb54f6eb92be7f78cbdc5 +dist/2025-01-08/clippy-beta-loongarch64-unknown-linux-musl.tar.xz=970c1e229fe35bbeb251e6e1291618d9da5334c1608685d8d602209bccafb4e8 +dist/2025-01-08/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=25ecca37c54464052f44b79ae061c992f9bb29a671881f5fdc7a8f4e1953e543 +dist/2025-01-08/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=e353972b9c73228c523077137a000d1c265dd24012fd1f3f44cb16ab172561c2 +dist/2025-01-08/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=b6d050a3cde8ff3c94dd072fce770ddf43486c8db37ca2ce2096f427f169cb81 +dist/2025-01-08/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=1728393681aa26afdd9b4c6147b1cd95a43e614cb379fd2f0e38fd68b26cd1fe +dist/2025-01-08/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=4136945a33e3658464835c0c31a244b953b1bc2614dd38266f650ee8e330d5fc +dist/2025-01-08/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=e121b45e188df1d540bd3d3d691b0b4043d5d067b52286e3e82c544d1f059a38 +dist/2025-01-08/clippy-beta-powerpc64le-unknown-linux-musl.tar.gz=6323723980ac6c17e951272bf5473b0f3735d05f00a469317d0371db9f1b562f +dist/2025-01-08/clippy-beta-powerpc64le-unknown-linux-musl.tar.xz=9f7120352e4b0e147159df5e04c1d1939dfe0e1081a6a0d8915ab75709147079 +dist/2025-01-08/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=32f52736c78922bb47a624365f3bd7a06d28581ef4f1fffcb57575b6de739431 +dist/2025-01-08/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=50c2b4e8c57bd0f4aed56d88e1bfbab2fc8f6c6e6d2aa8cf83dbb36067f2ca69 +dist/2025-01-08/clippy-beta-s390x-unknown-linux-gnu.tar.gz=5c2082cb9fa73e0af8b3384a3caf76c7dccbe7a78f553a5527b3539bbee83c0b +dist/2025-01-08/clippy-beta-s390x-unknown-linux-gnu.tar.xz=ca8c580219fe697b9dae674edb2a2c6ea6c737ebfc4d2580cb184745625874c0 +dist/2025-01-08/clippy-beta-x86_64-apple-darwin.tar.gz=83ac808792dadc4e5779c0b2185514de339cb61359b185d714713ab366909de4 +dist/2025-01-08/clippy-beta-x86_64-apple-darwin.tar.xz=c6a92d2b255736788cd53cb99b84e20ff135c4e5a7ed9cb2ccc9e5eb37523239 +dist/2025-01-08/clippy-beta-x86_64-pc-windows-gnu.tar.gz=6984f5b15e2f56e12924cb251d99f7b52307b1368c47ddc6ec4d17bbce8959e1 +dist/2025-01-08/clippy-beta-x86_64-pc-windows-gnu.tar.xz=fecd2eb58e8e1c9422e4433f120c3456f9cbd0f216863d9e3d37129bda966f79 +dist/2025-01-08/clippy-beta-x86_64-pc-windows-msvc.tar.gz=674115dee679648c45203b9014ce2aaf3812b3de079fc12aab4ba34aa4d8e8e1 +dist/2025-01-08/clippy-beta-x86_64-pc-windows-msvc.tar.xz=892ca2c9bb499ab0922919d89bba3c176e75570be2f4f2a1ac08608ba490d4cb +dist/2025-01-08/clippy-beta-x86_64-unknown-freebsd.tar.gz=513b3402e525d1e28c648a640d4506f9456c5c2fb45e2b7bc7f2c325a2d2f920 +dist/2025-01-08/clippy-beta-x86_64-unknown-freebsd.tar.xz=5e5a6c353aa3c8cfaafa694e70b69ed54a8c8dea5ea46412fd191a28be3bd926 +dist/2025-01-08/clippy-beta-x86_64-unknown-illumos.tar.gz=f16841404d8bb2aa1bd530eb6c2850e665c9e90f82eb6968cfcd4063b5ff8d10 +dist/2025-01-08/clippy-beta-x86_64-unknown-illumos.tar.xz=3c23e2df43a58499b4cf3652b2b02b1f68141b417829eb06d44c977c745fc7ff +dist/2025-01-08/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=43bda68cf861a73c8e9bb461e4c28d3c5315f04900db68168584db4f85e50492 +dist/2025-01-08/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=0d3bac2b017cb529d3fcf81042edae79362842f191a6759441afab41b19cd0eb +dist/2025-01-08/clippy-beta-x86_64-unknown-linux-musl.tar.gz=af7613a78ea98f2f134a69a32bdc09a103f83d6b1ea2ea3c27ddb3eb74430695 +dist/2025-01-08/clippy-beta-x86_64-unknown-linux-musl.tar.xz=78baca17912ad3cf49214187471ddcca890e787dc9821daf37103ea9f8632c1a +dist/2025-01-08/clippy-beta-x86_64-unknown-netbsd.tar.gz=f85ce010c7d235358e532b23e85ca8fd287376c311ca9427bd41ca0b3f04510b +dist/2025-01-08/clippy-beta-x86_64-unknown-netbsd.tar.xz=aacc604a5b53267a8eb936a433eba97ad4a002558836c933548152fc6cf50b0f +dist/2025-01-08/rustfmt-nightly-aarch64-apple-darwin.tar.gz=1ad6d3a92bfd2398af3f712b8238014b5517c80c54e2334e6bdf5bfec349bf0e +dist/2025-01-08/rustfmt-nightly-aarch64-apple-darwin.tar.xz=fcee1911fad43cb5689786b9603ca43fe2e6fccc89aec1f63b8afba4df915a71 +dist/2025-01-08/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=3d219a1e888acdccaf75afc7c7820f45eb2161a0c78304f76a28af95c5a53dde +dist/2025-01-08/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=0b4ea270256e8efb6df7d60473db256455fe7d5de835f164bdd0a92c3cea7369 +dist/2025-01-08/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=a7edc1692f4a5975fa5877e1af63c8e9c0a2d0946086413678973990fda8ee50 +dist/2025-01-08/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=bd4b98f560d00da97f2e7899ca32729581f9c5a386f398cf477c1ba6c922029e +dist/2025-01-08/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=49387f42c45fcb21d55e056843ec3ea210c89b10722adfd2ad6ee5d4a929f7c8 +dist/2025-01-08/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=778b995192beced6c690d80103c719e4b30c16b5e3caa2e2586693baac7ead18 +dist/2025-01-08/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=906191d5b2ca8e7645d6ff8571419aa8b50ca62fe10a1934722f1a493230da83 +dist/2025-01-08/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=6944b61661c3b1f72ec86a80b222bdc0a20e88a4e2a2f342bbb7ae2f8a1e1a6b +dist/2025-01-08/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=e7fd3ba048b6d20956f6a76b6e859204bb9847bbc1305482426bb40f5b28ad36 +dist/2025-01-08/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=312042e89eb0007fccdc5effa6098a762b94ff3a15840dee0fe681486b0ebda6 +dist/2025-01-08/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=584de19a62f85c418d01bc7943f0e3793f4c79776b8aeb5a7993cd1e7141bd7f +dist/2025-01-08/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=43327de3051396e27793390468003d1f13da0f0ed1cb7d5224ededf2787fefe8 +dist/2025-01-08/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=bbb72a900c77cebec9cae4b2c6fd0e3bce3451bb2fb65fabfb9f042a55163f78 +dist/2025-01-08/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=fbf8acb567141328ca628bd3c81af9477eb177bb398be3feef55d57acde2af5b +dist/2025-01-08/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=4a80751fdd514a71d20d440260a86510c83cadbeac3bf03014ef5377c6530e10 +dist/2025-01-08/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=fa3d56f5ec9f88448a2b6c36809b6c21bacb2cec7350571d386320aae2b410e5 +dist/2025-01-08/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=0f11501ddf7be92b987404df21fb4ed83ec8893d887943d86031709e7d671900 +dist/2025-01-08/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=264accec3e4ff18a3f21508bdc22988c86c0c899be83ed491f7f45f4d8d1f76e +dist/2025-01-08/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=a32c70abfec103c12119a5adceb83eae81b7fc950733e6039e0b3d3a589d16c5 +dist/2025-01-08/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=b0ed3c98ee0cabe8e85d031539796f0ff3e6fb3ed90275242d6fe9f55900abed +dist/2025-01-08/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.gz=b4f8456e420eea3025f32303cda5de26c67d061e04fb2229a247411ac7dd8351 +dist/2025-01-08/rustfmt-nightly-loongarch64-unknown-linux-musl.tar.xz=e66969acdf2d98ffd854f19e7d9f8beee8ec8fe7ed4493bd308b2f9b44cd9c90 +dist/2025-01-08/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=f97621947a0a90815867d7a0a08722f87cab26584a4b61dca7d81e50336b16cd +dist/2025-01-08/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=9af1743797ac19982892046c5d4d1865c0fa1e7cc0a38ef43f2364e215a2b5ad +dist/2025-01-08/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=fdf25dcd2c911c4fc6fbb3a184d078e36cd908a6a7b135c2a48a6f6efabcfd78 +dist/2025-01-08/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=6df2621585eb4d0c5be9b3118034b70dc12e3ac999a47e843b9b2c8ca48629c8 +dist/2025-01-08/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=bd6c79723c219e2c2b56bea90e3c2808bdf2119d299fb84d11d021895292d464 +dist/2025-01-08/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=4e1f249176e480b12ea11bb49377cda875189f382086339ed3ce591e3c7576e1 +dist/2025-01-08/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.gz=36877f682d6c43aaa755b5edb5599e9ee8f48969e93532f458f516ac052dd47b +dist/2025-01-08/rustfmt-nightly-powerpc64le-unknown-linux-musl.tar.xz=ef36ffefbe132c56c3a993ba4f8b9048f65ac87f3f22e9471064d3fa7c0e9bbd +dist/2025-01-08/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=2d9a0f3c007b59fb107c859ee4eab0ceadfe4c220ba45bc8c8f4bf5cb2608ba7 +dist/2025-01-08/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=f09cde2c36a4b9791f1492b0319ef1444418b2162215d48d18a6eb7493dd275a +dist/2025-01-08/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=585ef59a9fcb414ddec4fb69a6c33548a38b62c87be3c707443954c7be9eb764 +dist/2025-01-08/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=4da9c4095b0e30d61c9617a5cbfd69ebc77d54f248dfa3c2f9c096383184a762 +dist/2025-01-08/rustfmt-nightly-x86_64-apple-darwin.tar.gz=28ed0f8745b5b68527ca735a794b11a1411574cb0756b74b7e5ad1f0652f1670 +dist/2025-01-08/rustfmt-nightly-x86_64-apple-darwin.tar.xz=6b3c264c1e1002c381bc952ae5f24f56f7bd623067ef0f0179677798539d0aa9 +dist/2025-01-08/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=46b9ee6fe5c5bc6ec504b62fd9356cf467ea001cea073056fd8b53265b424940 +dist/2025-01-08/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=d888eaa7a4b42b4761cba736c75c29d047e31d6d3918c3d64e8347236918f035 +dist/2025-01-08/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=3b04474137945edf639d267a67712166c07df4c018276dfabbd7ad714c952750 +dist/2025-01-08/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=f5268dc4d20eb86ae882c5faffdc1b6dfee52ab4487569a02f92e2e001a936ba +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=6fe48737c200f3ab1ebea3b5089403ad76a8dad4754cbdfa274c89dbf9b0ea45 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=04f1a8d21dc54470df735f6b1f09e9d6e26c106d4ae4405962aaed11da80bea6 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=0dfa280e767a16665aad2bb6051b88a84850ccc408a24d99b89bf8d659149833 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=601215d564ed36cdd9be9abfc3ba42bbc15fc75d306e0921b36083a293067980 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=ce5f4421a454ac2e5f8e53d0ab6050be45f73a3931b9e60240b13f7bf06800ee +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=7cda6e670d0718d13b89225f58df1c6a2473e6d2626144354c854d671c778ba2 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=26e3275bab7d39c465bab76074dd54f534980c89f5460838da9745c171859604 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=b33178fff841b91968c2f6fc108389a1740d865214729406de40b49e6969d22f +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=cc8439ce6628f864503c7ca322ec0607000536c002895dc872ad1bca8e865119 +dist/2025-01-08/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=2d3c2f666bf57384e11f12dcb00346f73953ca8a2a60947ae5c0b14b856b9170 +dist/2025-01-08/rustc-nightly-aarch64-apple-darwin.tar.gz=febc42bc953bf6b23253670756fb294545a1db22b02fbe2273e1030a78f0dc8a +dist/2025-01-08/rustc-nightly-aarch64-apple-darwin.tar.xz=f978d9d9e0bcc75efd4250406b796fb3f6cae31ec41ca2a40f88f7e70201171c +dist/2025-01-08/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=6dfd42938c385aba18ba477e4430dd89e9b780d9217f410f68094d7bbbc722a3 +dist/2025-01-08/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=d79b6f6158a99ea82314a99a68c52c63deb16d777d74426780df06c5110576a5 +dist/2025-01-08/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=5dfaa19982b344ba8d3f59418a847a764eb8b6b4a9809b8a63aeafd4725cb406 +dist/2025-01-08/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=dbe770d4594c9f8cd27aec8d342fd5f6bb44570723228726cbf19424dc0d7708 +dist/2025-01-08/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=41357e54d974f3dfe0e71cac0eedc69625ddf7e2a1cf563c2e9689afa53cef11 +dist/2025-01-08/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=208ffbee7b68121ddc0cb92dc1ab73dc3b489869ab190abd99174026724a3493 +dist/2025-01-08/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=7970e126f177ea336cc243448d034c8ad1aa6489db36ea0bde54e2df472552df +dist/2025-01-08/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=8333efc4f3bf9d6fb01a947a6f631f03fd506a494060e426ee7621831dfc3a95 +dist/2025-01-08/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=f8b9030db15e6004647772700a7b137fe9e7ff1c24fa0742c45a25d072e1f1e4 +dist/2025-01-08/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=f554b976502cdc78735def853947d9b48ab1ff1aca208cb348efdedae0df407d +dist/2025-01-08/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=402109bd4811e723999c8747978452e2a96e2ddf1109393302f9c1be7e36348e +dist/2025-01-08/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=b2dd7f94bbc34b91fd2dc2c1bab7179a51265bc51ac58b66c8aedbe849103749 +dist/2025-01-08/rustc-nightly-i686-pc-windows-gnu.tar.gz=9e3f1aa453de7bfd9a15f1925d96b46ce6375b49c6656db14bd2ed99f4138e1c +dist/2025-01-08/rustc-nightly-i686-pc-windows-gnu.tar.xz=468a157752c69bb2096ffe91413086d04eef8c64b9cdc44b51d7e9a94d2f4b5e +dist/2025-01-08/rustc-nightly-i686-pc-windows-msvc.tar.gz=1a9977db5843941c49c808830364e32c7e57190d3bf4dcf74e2a109473f808c0 +dist/2025-01-08/rustc-nightly-i686-pc-windows-msvc.tar.xz=7cc68c40929538b7711f5bccfa964841094eed9859f490846de0db0b2ba74343 +dist/2025-01-08/rustc-nightly-i686-unknown-linux-gnu.tar.gz=08c86c8984b966d3943de2252318aad2a27899fb58c5eb1079b8b8164b56d21f +dist/2025-01-08/rustc-nightly-i686-unknown-linux-gnu.tar.xz=d3b53723f085887709951dae7b0389b8cd4159a22fc7189bf7d7bc15ecb4bc57 +dist/2025-01-08/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=515db325eb12b211d08367436fd07f5ceff4743930b9e685b0b13b94b99bfa34 +dist/2025-01-08/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=b4986e7aca0325059689f8d7ead67616b2965922e77da33d9d03a7235520b585 +dist/2025-01-08/rustc-nightly-loongarch64-unknown-linux-musl.tar.gz=541571e5deb51d360eeaf2c88a28cc25c98dead983a3b8f2a6e3f371df3ce0cb +dist/2025-01-08/rustc-nightly-loongarch64-unknown-linux-musl.tar.xz=44fa8d77bf945b1a5ea9ed2a7e8228c8c0d5770098bbd8687173dff58b7005a1 +dist/2025-01-08/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=1ecae3935e253cc9b7aa44c8e7835fdf039d44ae19080e2316421b47453e3527 +dist/2025-01-08/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=b512b895e9b78069e57fce8c1982ea6fb8e463fbfe626474a6399008d5c149cf +dist/2025-01-08/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=91c4ddebe0b50e8778297793d77a583b23b6c333d785564b97ea38a137c3b7df +dist/2025-01-08/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=562f81acf112aefc6878132d7cf352da64b0eb1803d8c371e6713e270bea15bc +dist/2025-01-08/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=147bcd50f63f068a5814200ffeb8adc775c65187fb9c178dae5468af3fc04611 +dist/2025-01-08/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=9b6e863729599a96547119c1c2a04f23167cb142447644b88ccaa86d073e1471 +dist/2025-01-08/rustc-nightly-powerpc64le-unknown-linux-musl.tar.gz=85c085bf2771eb79dd3aca31ff9896de969de229c0549bd8c89a539393ef5cab +dist/2025-01-08/rustc-nightly-powerpc64le-unknown-linux-musl.tar.xz=107bba3499f4446aec6d79919453341a0d10fa5e6001da4cc9d3f1a618871989 +dist/2025-01-08/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=ca6663608404909610479a33562049d313882ab8252fd302657598b0251746ad +dist/2025-01-08/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=f66d3d9a1d897346945b254aed29e29b82be1255908dd0accc7deebd698a0b78 +dist/2025-01-08/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=480fdbaad5a5e9d6412682c61de324ac37093f60377e5f772c59dab58e63e6a2 +dist/2025-01-08/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=c438d14d90ed5acfa2108788b7179aebd7fcbac10703b2c5207ed7eaf988ddeb +dist/2025-01-08/rustc-nightly-x86_64-apple-darwin.tar.gz=b91f15c087c859b8ea6a5d556835c296ff480f6927efa9e535b9dcd726c8c4de +dist/2025-01-08/rustc-nightly-x86_64-apple-darwin.tar.xz=64b1129ba56b964ad433a499b2b5dd590371a4a7fa0f859c551bced786d876f4 +dist/2025-01-08/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=c90d259076abbb62f26348a2bfaf5bc1dca1e6ba7d4c1dec8f02d9dfaa21cc53 +dist/2025-01-08/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=127c2335d668cb93169681ec3775e0c9a91e0c5e1dd6043bd9236e18b7dedbf1 +dist/2025-01-08/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=a0dd89c80a4c2c724a8910c33c6d5159beef9b462f912326c659defb87ebe41e +dist/2025-01-08/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=b2c1972028521d6cd643a7a1f4db2eca1da5eb8ea03187bab5a7bef63377eec4 +dist/2025-01-08/rustc-nightly-x86_64-unknown-freebsd.tar.gz=3eb87ac0585b6a4a3cd0525e3f7fc04eafcb28b64ddb866beedbf24401810bf6 +dist/2025-01-08/rustc-nightly-x86_64-unknown-freebsd.tar.xz=fa93c78e53bc3e408c1b6d7917e643fb7f1f6203ec4a44036e8b73d37e650b82 +dist/2025-01-08/rustc-nightly-x86_64-unknown-illumos.tar.gz=3017ba6134aaf6cae0b7980ef211420ae1c09d230fc082e9f68a5a1a7335aad2 +dist/2025-01-08/rustc-nightly-x86_64-unknown-illumos.tar.xz=5a5881f7883eeb638c00ccc7a4cf453ddb987983228b066ec955b92d4e4855a7 +dist/2025-01-08/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=9c7796ef52678a29ea7e0406c1f2273ee11d907b379b3616237df9a05505140e +dist/2025-01-08/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=0c978e79f9794221ebd7c0fc0d4313e323d1aef1a5358ffc9cb806fcd997e4ef +dist/2025-01-08/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=21ff96c7b7c0a5a18051c12f01b67e90fef620c887f7d91e78944b5a1a0e8838 +dist/2025-01-08/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=2b7ffa8d546ed9b89472fd25ac56b4f6e968e99bfae9045a4019fab319716cf4 +dist/2025-01-08/rustc-nightly-x86_64-unknown-netbsd.tar.gz=ea30e5cef50b7b207844bc30ceaee4bcd30cde86f636080b243eb3e7e5367042 +dist/2025-01-08/rustc-nightly-x86_64-unknown-netbsd.tar.xz=c07dc61336037f475cb74945c0925fabd1fb33412eb4e6eba4004f207db67f3e \ No newline at end of file diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 561b611148a2..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", @@ -128,6 +129,8 @@ static TARGETS: &[&str] = &[ "mipsisa64r6el-unknown-linux-gnuabi64", "mipsel-unknown-linux-gnu", "mipsel-unknown-linux-musl", + "mips-mti-none-elf", + "mipsel-mti-none-elf", "nvptx64-nvidia-cuda", "powerpc-unknown-linux-gnu", "powerpc64-unknown-linux-gnu", diff --git a/src/tools/cargo b/src/tools/cargo index fd784878cfa8..088d49608272 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit fd784878cfa843e3e29a6654ecf564c62fae6735 +Subproject commit 088d496082726091024f1689c124a0c3dccbd775 diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml index bcb3193ad670..d6534fbaff94 100644 --- a/src/tools/clippy/.github/workflows/clippy_dev.yml +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -17,6 +17,9 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # Run - name: Build diff --git a/src/tools/clippy/.github/workflows/clippy_mq.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml index 496220480508..dee7d028655e 100644 --- a/src/tools/clippy/.github/workflows/clippy_mq.yml +++ b/src/tools/clippy/.github/workflows/clippy_mq.yml @@ -23,6 +23,8 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.ref }} + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # Run - name: Check Changelog @@ -63,6 +65,8 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install i686 dependencies if: matrix.host == 'i686-unknown-linux-gnu' @@ -74,7 +78,8 @@ jobs: - name: Install toolchain run: | rustup set default-host ${{ matrix.host }} - rustup show active-toolchain + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Run - name: Build @@ -121,9 +126,13 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install - name: Test metadata collection run: cargo collect-metadata @@ -136,9 +145,13 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Run - name: Build Integration Test @@ -188,9 +201,13 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Download - name: Download target dir @@ -205,7 +222,7 @@ jobs: # Run - name: Test ${{ matrix.integration }} run: | - TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ') + TOOLCHAIN=$(rustup show active-toolchain | head -n 1 | cut -f1 -d' ') rustup run $TOOLCHAIN $CARGO_TARGET_DIR/debug/integration --show-output env: INTEGRATION: ${{ matrix.integration }} diff --git a/src/tools/clippy/.github/workflows/clippy_pr.yml b/src/tools/clippy/.github/workflows/clippy_pr.yml index 2e5b5bd41dfb..80523d91f4fc 100644 --- a/src/tools/clippy/.github/workflows/clippy_pr.yml +++ b/src/tools/clippy/.github/workflows/clippy_pr.yml @@ -25,9 +25,14 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Run - name: Build diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml index 32dc251c836f..b42f3e7712f1 100644 --- a/src/tools/clippy/.github/workflows/deploy.yml +++ b/src/tools/clippy/.github/workflows/deploy.yml @@ -22,19 +22,27 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Checkout uses: actions/checkout@v4 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # Run - name: Set tag name if: startswith(github.ref, 'refs/tags/') run: | - TAG=$(basename ${{ github.ref }}) + TAG=$(basename "${TAGNAME}") echo "TAG_NAME=$TAG" >> $GITHUB_ENV + env: + # Make sure that the reference gets expanded before injecting it + TAGNAME: ${{ github.ref }} - name: Set beta to true if: github.ref == 'refs/heads/beta' run: echo "BETA=true" >> $GITHUB_ENV diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index 3cbda0b38243..64966f1d1898 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -21,6 +21,8 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 2 + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # HEAD is the generated merge commit `refs/pull/N/merge` between the PR and `master`, `HEAD^` # being the commit from `master` that is the base of the merge @@ -73,6 +75,9 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Cache lintcheck bin id: cache-lintcheck-bin @@ -103,6 +108,9 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Restore lintcheck bin uses: actions/cache/restore@v4 diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index 0d402fe70641..69d00dc027e8 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -12,6 +12,9 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index b6033de93501..1770e8095a01 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,52 @@ document. ## Unreleased / Beta / In Rust Nightly -[aa0d5513...master](https://github.com/rust-lang/rust-clippy/compare/aa0d5513...master) +[786fbd6d...master](https://github.com/rust-lang/rust-clippy/compare/786fbd6d...master) + +## Rust 1.84 + +Current stable, released 2025-01-09 + +[View all 84 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-10-03T21%3A23%3A58Z..2024-11-14T17%3A41%3A37Z+base%3Amaster) + +### New Lints + +* Added [`unnecessary_map_or`] to `style` + [#11796](https://github.com/rust-lang/rust-clippy/pull/11796) +* Added [`arbitrary_source_item_ordering`] to `restriction` + [#13376](https://github.com/rust-lang/rust-clippy/pull/13376) +* Added [`map_with_unused_argument_over_ranges`] to `restriction` + [#13034](https://github.com/rust-lang/rust-clippy/pull/13034) +* Added [`map_all_any_identity`] to `complexity` + [#13499](https://github.com/rust-lang/rust-clippy/pull/13499) +* Added [`needless_as_bytes`] to `complexity` + [#13437](https://github.com/rust-lang/rust-clippy/pull/13437) +* Added [`unnecessary_literal_bound`] to `pedantic` + [#13395](https://github.com/rust-lang/rust-clippy/pull/13395) +* Added [`manual_ignore_case_cmp`] to `perf` + [#13334](https://github.com/rust-lang/rust-clippy/pull/13334) +* Added [`regex_creation_in_loops`] to `perf` + [#13412](https://github.com/rust-lang/rust-clippy/pull/13412) + +### Moves and Deprecations + +* Moved [`manual_is_power_of_two`] to `pedantic` (From `complexity`, now allow-by-default) + [#13553](https://github.com/rust-lang/rust-clippy/pull/13553) +* Move [`module_name_repetitions`] to `restriction` (from `pedantic`) + [#13541](https://github.com/rust-lang/rust-clippy/pull/13541) + +### Enhancements + +* [`doc_markdown`]: Added the following identifiers to [`doc-valid-idents`]: + CoAP, MHz, GHz, and THz + [#13633](https://github.com/rust-lang/rust-clippy/pull/13633) + [#13460](https://github.com/rust-lang/rust-clippy/pull/13460) +* [`large_const_arrays`]: Changed the default of [`array-size-threshold`] to `16kb` (from `512kb`) + [#13485](https://github.com/rust-lang/rust-clippy/pull/13485) ## Rust 1.83 -Current stable, released 2024-11-28 +Released 2024-11-28 [View all 64 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-08-25T09%3A59%3A01Z..2024-10-03T13%3A42%3A56Z+base%3Amaster) @@ -5493,6 +5534,7 @@ Released 2018-09-13 [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons +[`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens @@ -6252,6 +6294,7 @@ Released 2018-09-13 [`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold [`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold +[`lint-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-inconsistent-struct-field-initializers [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold [`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else [`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 77cb0006ff85..efa59fecc0e8 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md index df9b1bbe18f3..2b2c096b0496 100644 --- a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md +++ b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md @@ -83,7 +83,12 @@ As section headers, we use: ``` ### New Lints +* Added [`LINT`] to `GROUP` + ### Moves and Deprecations +* Moved [`LINT`] to `GROUP` (From `GROUP`, now LEVEL-by-default) +* Renamed `LINT` to [`LINT`] + ### Enhancements ### False Positive Fixes ### Suggestion Fixes/Improvements diff --git a/src/tools/clippy/book/src/development/method_checking.md b/src/tools/clippy/book/src/development/method_checking.md index 9c5d4b516db2..b3126024b990 100644 --- a/src/tools/clippy/book/src/development/method_checking.md +++ b/src/tools/clippy/book/src/development/method_checking.md @@ -21,7 +21,7 @@ use clippy_utils::is_trait_method; impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // Check our expr is calling a method with pattern matching - if let hir::ExprKind::MethodCall(path, _, [self_arg, ..]) = &expr.kind + if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind // Check if the name of this method is `our_fancy_method` && path.ident.name.as_str() == "our_fancy_method" // We can check the type of the self argument whenever necessary. diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index ea1d7d11389d..181e794e6e46 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -582,6 +582,33 @@ The maximum size of the `Err`-variant in a `Result` returned from a function * [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) +## `lint-inconsistent-struct-field-initializers` +Whether to suggest reordering constructor fields when initializers are present. + +Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the +suggested code would compile, it can change semantics if the initializer expressions have side effects. The +following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + +```rust +struct MyStruct { + vector: Vec, + length: usize +} +fn main() { + let vector = vec![1,2,3]; + MyStruct { length: vector.len(), vector}; +} +``` + +[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + +**Default Value:** `false` + +--- +**Affected lints:** +* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor) + + ## `literal-representation-threshold` The lower bound for linting decimal literals diff --git a/src/tools/clippy/clippy.toml b/src/tools/clippy/clippy.toml index a7b0cc56ea12..f4789c9d0303 100644 --- a/src/tools/clippy/clippy.toml +++ b/src/tools/clippy/clippy.toml @@ -1,5 +1,7 @@ avoid-breaking-exported-api = false +lint-inconsistent-struct-field-initializers = true + [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 3f18a0bc7d25..c761e207c6b6 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_config" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index bffa04f6f090..c616589c56e0 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -532,6 +532,26 @@ define_Conf! { /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] large_error_threshold: u64 = 128, + /// Whether to suggest reordering constructor fields when initializers are present. + /// + /// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the + /// suggested code would compile, it can change semantics if the initializer expressions have side effects. The + /// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + /// + /// ```rust + /// struct MyStruct { + /// vector: Vec, + /// length: usize + /// } + /// fn main() { + /// let vector = vec![1,2,3]; + /// MyStruct { length: vector.len(), vector}; + /// } + /// ``` + /// + /// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + #[lints(inconsistent_struct_constructor)] + lint_inconsistent_struct_field_initializers: bool = false, /// The lower bound for linting decimal literals #[lints(decimal_literal_representation)] literal_representation_threshold: u64 = 16384, diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index c66738592820..790dafa811f9 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -179,8 +179,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { #[expect(clippy::drain_collect)] fields.push(ClippyConf { name, - lints: lints.drain(..).collect(), attrs: &conf[attrs_start..attrs_end], + lints: lints.drain(..).collect(), field: conf[field_start..i].trim_end(), }); attrs_start = i; @@ -191,8 +191,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { #[expect(clippy::drain_collect)] fields.push(ClippyConf { name, - lints: lints.drain(..).collect(), attrs: &conf[attrs_start..attrs_end], + lints: lints.drain(..).collect(), field: conf[field_start..i].trim_end(), }); attrs_start = i; @@ -220,8 +220,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { } fields.push(ClippyConf { name, - lints, attrs: &conf[attrs_start..attrs_end], + lints, field: conf[field_start..].trim_end(), }); diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index c1f8e82f6988..b575ac1bf4cc 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index 741539902662..380b094d017e 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -428,8 +428,8 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { // Makes a note of the current item for comparison with the next. cur_t = Some(CurItem { - order: module_level_order, item, + order: module_level_order, name: get_item_name(item), }); } @@ -464,7 +464,7 @@ fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleIte ItemKind::Use(..) => Use, ItemKind::Static(..) => Static, ItemKind::Const(..) => Const, - ItemKind::Fn{ .. } => Fn, + ItemKind::Fn { .. } => Fn, ItemKind::Macro(..) => Macro, ItemKind::Mod(..) => Mod, ItemKind::ForeignMod { .. } => ForeignMod, diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index 32c28c09c360..8c91c65eaf76 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -66,7 +66,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item_span: Span, attrs: &[Attribute]) fn lint_mixed_attrs(cx: &EarlyContext<'_>, attrs: &[Attribute]) { let mut attrs_iter = attrs.iter().filter(|attr| !attr.span.from_expansion()); - let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.last()) { + let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.next_back()) { first.span.with_hi(last.span.hi()) } else { return; diff --git a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs index 67aa33ca06c3..6057144bc6a4 100644 --- a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs @@ -1,11 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::Msrv; -use clippy_utils::source::snippet_with_context; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::{is_lint_allowed, msrvs, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; +use rustc_span::BytePos; use super::BORROW_AS_PTR; @@ -32,12 +34,21 @@ pub(super) fn check<'tcx>( return false; } - let suggestion = if msrv.meets(msrvs::RAW_REF_OP) { + let (suggestion, span) = if msrv.meets(msrvs::RAW_REF_OP) { let operator_kind = match mutability { Mutability::Not => "const", Mutability::Mut => "mut", }; - format!("&raw {operator_kind} {snip}") + // Make sure that the span to be replaced doesn't include parentheses, that could break the + // suggestion. + let span = if has_enclosing_paren(snippet_with_applicability(cx, expr.span, "", &mut app)) { + expr.span + .with_lo(expr.span.lo() + BytePos(1)) + .with_hi(expr.span.hi() - BytePos(1)) + } else { + expr.span + }; + (format!("&raw {operator_kind} {snip}"), span) } else { let Some(std_or_core) = std_or_core(cx) else { return false; @@ -46,18 +57,10 @@ pub(super) fn check<'tcx>( Mutability::Not => "addr_of", Mutability::Mut => "addr_of_mut", }; - format!("{std_or_core}::ptr::{macro_name}!({snip})") + (format!("{std_or_core}::ptr::{macro_name}!({snip})"), expr.span) }; - span_lint_and_sugg( - cx, - BORROW_AS_PTR, - expr.span, - "borrow as raw pointer", - "try", - suggestion, - Applicability::MachineApplicable, - ); + span_lint_and_sugg(cx, BORROW_AS_PTR, span, "borrow as raw pointer", "try", suggestion, app); return true; } false diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index c64c0e15144d..d90cf124fe42 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -836,11 +836,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { as_underscore::check(cx, expr, cast_to_hir); as_pointer_underscore::check(cx, cast_to, cast_to_hir); - let was_borrow_as_ptr_emitted = if self.msrv.meets(msrvs::BORROW_AS_PTR) { - borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, &self.msrv) - } else { - false - }; + let was_borrow_as_ptr_emitted = self.msrv.meets(msrvs::BORROW_AS_PTR) + && borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, &self.msrv); if self.msrv.meets(msrvs::PTR_FROM_REF) && !was_borrow_as_ptr_emitted { ref_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir); } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 7451fb909ef8..3ff10d850f82 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -372,6 +372,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::CLONE_ON_REF_PTR_INFO, crate::methods::COLLAPSIBLE_STR_REPLACE_INFO, crate::methods::CONST_IS_EMPTY_INFO, + crate::methods::DOUBLE_ENDED_ITERATOR_LAST_INFO, crate::methods::DRAIN_COLLECT_INFO, crate::methods::ERR_EXPECT_INFO, crate::methods::EXPECT_FUN_CALL_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 3b3a78cb1153..c04a73c890f0 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -3,8 +3,11 @@ use clippy_utils::numeric_literal; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt}; -use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind, StructTailExpr}; +use rustc_hir::intravisit::{Visitor, walk_expr, walk_pat, walk_stmt}; +use rustc_hir::{ + Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Pat, PatExpr, PatExprKind, PatKind, Stmt, StmtKind, + StructTailExpr, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty}; @@ -219,6 +222,22 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> { walk_expr(self, expr); } + fn visit_pat(&mut self, pat: &'tcx Pat<'_>) { + match pat.kind { + PatKind::Expr(&PatExpr { + hir_id, + kind: PatExprKind::Lit { lit, .. }, + .. + }) => { + let ty = self.cx.typeck_results().node_type(hir_id); + self.check_lit(lit, ty, hir_id); + return; + }, + _ => {}, + } + walk_pat(self, pat) + } + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { match stmt.kind { // we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric` diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index 40377dd841e0..3e2b7055de4d 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -1,6 +1,6 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait_with_env, is_type_diagnostic_item}; use clippy_utils::{is_doc_hidden, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; @@ -70,7 +70,14 @@ pub fn check( && let typeck = cx.tcx.typeck_body(body_id) && let body = cx.tcx.hir().body(body_id) && let ret_ty = typeck.expr_ty(body.value) - && implements_trait(cx, ret_ty, future, &[]) + && implements_trait_with_env( + cx.tcx, + ty::TypingEnv::non_body_analysis(cx.tcx, owner_id.def_id), + ret_ty, + future, + Some(owner_id.def_id.to_def_id()), + &[], + ) && let ty::Coroutine(_, subs) = ret_ty.kind() && is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result) { diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index c835b81679b5..7561a6cf2a78 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -797,8 +797,8 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ parser.into_offset_iter(), &doc, Fragments { - fragments: &fragments, doc: &doc, + fragments: &fragments, }, )) } diff --git a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index 2327da0ccff7..1f89cab91480 100644 --- a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -25,7 +25,7 @@ pub(super) fn check( // page. So associated items or impl blocks are not part of this list. ItemKind::Static(..) | ItemKind::Const(..) - | ItemKind::Fn{ .. } + | ItemKind::Fn { .. } | ItemKind::Macro(..) | ItemKind::Mod(..) | ItemKind::TyAlias(..) diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index 70524e458c78..2bec4f2f99e5 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -678,12 +678,12 @@ fn find_insert_calls<'tcx>( map: contains_expr.map, key: contains_expr.key, ctxt: expr.span.ctxt(), - edits: Vec::new(), - is_map_used: false, allow_insert_closure: true, can_use_entry: true, in_tail_pos: true, is_single_insert: true, + is_map_used: false, + edits: Vec::new(), loops: Vec::new(), locals: HirIdSet::default(), }; 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..8a5cf7f56d5f 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -55,8 +55,8 @@ 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::Path(_) | PatKind::Lit(_) => true, + PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), + PatKind::Path(_) | PatKind::Expr(_) => true, } } diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 2cd48ef98e52..c0b4743fd71c 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -182,7 +182,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc // will succeed iff `T: 'static`. But the region of `T` is always erased by `typeck.expr_ty()` when // T is a generic type. For example, return type of `Option::as_deref()` is a generic. // So we have a hack like this. - && generic_args.len() > 0 + && !generic_args.is_empty() { return; } diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index 4427edb752e0..ef272c305d34 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -243,11 +243,11 @@ fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds && !predicates.is_empty() { Some(ImplTraitBound { + span: bound.span(), predicates, + trait_def_id, args: path.args.map_or([].as_slice(), |p| p.args), constraints: path.args.map_or([].as_slice(), |p| p.constraints), - trait_def_id, - span: bound.span(), }) } else { None diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index 4fcd2abb7694..39ff3c13bcce 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,19 +1,21 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::fulfill_or_allowed; use clippy_utils::source::snippet; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::{self as hir, ExprKind, StructTailExpr}; +use rustc_hir::{self as hir, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_middle::ty::TyCtxt; +use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::symbol::Symbol; -use std::fmt::{self, Write as _}; declare_clippy_lint! { /// ### What it does - /// Checks for struct constructors where all fields are shorthand and - /// the order of the field init shorthand in the constructor is inconsistent - /// with the order in the struct definition. + /// Checks for struct constructors where the order of the field + /// init in the constructor is inconsistent with the order in the + /// struct definition. /// /// ### Why is this bad? /// Since the order of fields in a constructor doesn't affect the @@ -59,16 +61,37 @@ declare_clippy_lint! { #[clippy::version = "1.52.0"] pub INCONSISTENT_STRUCT_CONSTRUCTOR, pedantic, - "the order of the field init shorthand is inconsistent with the order in the struct definition" + "the order of the field init is inconsistent with the order in the struct definition" } -declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); +pub struct InconsistentStructConstructor { + lint_inconsistent_struct_field_initializers: bool, +} + +impl InconsistentStructConstructor { + pub fn new(conf: &'static Conf) -> Self { + Self { + lint_inconsistent_struct_field_initializers: conf.lint_inconsistent_struct_field_initializers, + } + } +} + +impl_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if let ExprKind::Struct(qpath, fields, base) = expr.kind - && fields.iter().all(|f| f.is_shorthand) - && !expr.span.from_expansion() + let ExprKind::Struct(_, fields, _) = expr.kind else { + return; + }; + let all_fields_are_shorthand = fields.iter().all(|f| f.is_shorthand); + let applicability = if all_fields_are_shorthand { + Applicability::MachineApplicable + } else if self.lint_inconsistent_struct_field_initializers { + Applicability::MaybeIncorrect + } else { + return; + }; + if !expr.span.from_expansion() && let ty = cx.typeck_results().expr_ty(expr) && let Some(adt_def) = ty.ty_adt_def() && adt_def.is_struct() @@ -85,36 +108,24 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { return; } - let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect(); - ordered_fields.sort_unstable_by_key(|id| def_order_map[id]); - - let mut fields_snippet = String::new(); - let (last_ident, idents) = ordered_fields.split_last().unwrap(); - for ident in idents { - let _: fmt::Result = write!(fields_snippet, "{ident}, "); - } - fields_snippet.push_str(&last_ident.to_string()); - - let base_snippet = if let StructTailExpr::Base(base) = base { - format!(", ..{}", snippet(cx, base.span, "..")) - } else { - String::new() - }; - - let sugg = format!( - "{} {{ {fields_snippet}{base_snippet} }}", - snippet(cx, qpath.span(), ".."), - ); + let span = field_with_attrs_span(cx.tcx, fields.first().unwrap()) + .with_hi(field_with_attrs_span(cx.tcx, fields.last().unwrap()).hi()); if !fulfill_or_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, Some(ty_hir_id)) { - span_lint_and_sugg( + span_lint_and_then( cx, INCONSISTENT_STRUCT_CONSTRUCTOR, - expr.span, + span, "struct constructor field order is inconsistent with struct definition field order", - "try", - sugg, - Applicability::MachineApplicable, + |diag| { + let msg = if all_fields_are_shorthand { + "try" + } else { + "if the field evaluation order doesn't matter, try" + }; + let sugg = suggestion(cx, fields, &def_order_map); + diag.span_suggestion(span, msg, sugg, applicability); + }, ); } } @@ -135,3 +146,45 @@ fn is_consistent_order<'tcx>(fields: &'tcx [hir::ExprField<'tcx>], def_order_map true } + +fn suggestion<'tcx>( + cx: &LateContext<'_>, + fields: &'tcx [hir::ExprField<'tcx>], + def_order_map: &FxHashMap, +) -> String { + let ws = fields + .windows(2) + .map(|w| { + let w0_span = field_with_attrs_span(cx.tcx, &w[0]); + let w1_span = field_with_attrs_span(cx.tcx, &w[1]); + let span = w0_span.between(w1_span); + snippet(cx, span, " ") + }) + .collect::>(); + + let mut fields = fields.to_vec(); + fields.sort_unstable_by_key(|field| def_order_map[&field.ident.name]); + let field_snippets = fields + .iter() + .map(|field| snippet(cx, field_with_attrs_span(cx.tcx, field), "..")) + .collect::>(); + + assert_eq!(field_snippets.len(), ws.len() + 1); + + let mut sugg = String::new(); + for i in 0..field_snippets.len() { + sugg += &field_snippets[i]; + if i < ws.len() { + sugg += &ws[i]; + } + } + sugg +} + +fn field_with_attrs_span(tcx: TyCtxt<'_>, field: &hir::ExprField<'_>) -> Span { + if let Some(attr) = tcx.hir().attrs(field.hir_id).first() { + field.span.with_lo(attr.span.lo()) + } else { + field.span + } +} diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 3ea758e176f0..5418acc105eb 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; +use clippy_utils::ty::implements_trait; use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, is_trait_method, peel_ref_operators}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -8,8 +9,8 @@ use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, - ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatKind, PathSegment, PrimTy, QPath, - TraitItemRef, TyKind, + ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy, + QPath, TraitItemRef, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; @@ -163,7 +164,13 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if let ExprKind::Let(lt) = expr.kind && match lt.pat.kind { PatKind::Slice([], None, []) => true, - PatKind::Lit(lit) if is_empty_string(lit) => true, + PatKind::Expr(lit) => match lit.kind { + PatExprKind::Lit { lit, .. } => match lit.node { + LitKind::Str(lit, _) => lit.as_str().is_empty(), + _ => false, + }, + _ => false, + }, _ => false, } && !expr.span.from_expansion() @@ -620,18 +627,30 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) } - let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); - match ty.kind() { - ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { - let is_empty = sym!(is_empty); - cx.tcx - .associated_items(principal.def_id()) - .filter_by_name_unhygienic(is_empty) - .any(|item| is_is_empty(cx, item)) - }), - ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), - ty::Adt(id, _) => has_is_empty_impl(cx, id.did()), - ty::Array(..) | ty::Slice(..) | ty::Str => true, - _ => false, + fn ty_has_is_empty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, depth: usize) -> bool { + match ty.kind() { + ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { + let is_empty = sym!(is_empty); + cx.tcx + .associated_items(principal.def_id()) + .filter_by_name_unhygienic(is_empty) + .any(|item| is_is_empty(cx, item)) + }), + ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), + ty::Adt(id, _) => { + has_is_empty_impl(cx, id.did()) + || (cx.tcx.recursion_limit().value_within_limit(depth) + && cx.tcx.get_diagnostic_item(sym::Deref).is_some_and(|deref_id| { + implements_trait(cx, ty, deref_id, &[]) + && cx + .get_associated_type(ty, deref_id, "Target") + .is_some_and(|deref_ty| ty_has_is_empty(cx, deref_ty, depth + 1)) + })) + }, + ty::Array(..) | ty::Slice(..) | ty::Str => true, + _ => false, + } } + + ty_has_is_empty(cx, cx.typeck_results().expr_ty(expr).peel_refs(), 0) } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index d33013ba6638..fad6f9d08803 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -649,7 +649,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn)); store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))); store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)); - store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor)); + store.register_late_pass(move |_| { + Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new( + conf, + )) + }); store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(conf))); diff --git a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs index f88605fbea0d..6be30f3c957b 100644 --- a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs @@ -38,8 +38,8 @@ pub(super) fn check<'tcx>( cx, label, inner_labels: label.into_iter().collect(), - is_finite: false, loop_depth: 0, + is_finite: false, }; loop_visitor.visit_block(loop_block); diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs index 312bcb55a953..006addb987f5 100644 --- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -220,11 +220,11 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { // `check_stmt_post` on `(Late)LintPass`, which we'd need to detect when we're leaving a macro span let mut vis = BodyVisitor { + macro_unsafe_blocks: Vec::new(), #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning expn_depth: if body.value.span.from_expansion() { 1 } else { 0 }, - macro_unsafe_blocks: Vec::new(), - lint: self, - cx + cx, + lint: self }; vis.visit_body(body); } diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs index bbb89bee8355..aa59b047b169 100644 --- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs @@ -2,14 +2,15 @@ use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::sugg::Sugg; -use rustc_ast::{BinOpKind, LitKind}; +use clippy_utils::sugg::{Sugg, has_enclosing_paren}; +use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self}; use rustc_session::impl_lint_pass; +use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use clippy_config::Conf; @@ -138,9 +139,40 @@ fn build_suggestion( applicability: &mut Applicability, ) { let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_par(); + let type_suffix = if cx.typeck_results().expr_ty(lhs).is_numeric() + && matches!( + lhs.kind, + ExprKind::Lit(Spanned { + node: LitKind::Int(_, LitIntType::Unsuffixed), + .. + }) | ExprKind::Unary(UnOp::Neg, Expr { + kind: ExprKind::Lit(Spanned { + node: LitKind::Int(_, LitIntType::Unsuffixed), + .. + }), + .. + }) + ) { + format!("_{}", cx.typeck_results().expr_ty(rhs)) + } else { + String::new() + }; + let dividend_sugg_str = dividend_sugg.into_string(); + // If `dividend_sugg` has enclosing paren like `(-2048)` and we need to add type suffix in the + // suggestion message, we want to make a suggestion string before `div_ceil` like + // `(-2048_{type_suffix})`. + let suggestion_before_div_ceil = if has_enclosing_paren(÷nd_sugg_str) { + format!( + "{}{})", + ÷nd_sugg_str[..dividend_sugg_str.len() - 1].to_string(), + type_suffix + ) + } else { + format!("{dividend_sugg_str}{type_suffix}") + }; let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability); - let sugg = format!("{dividend_sugg}.div_ceil({divisor_snippet})"); + let sugg = format!("{suggestion_before_div_ceil}.div_ceil({divisor_snippet})"); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs index dabfac3f6137..506f4f6d9de1 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs @@ -32,7 +32,7 @@ declare_clippy_lint! { /// a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc") /// } /// ``` - #[clippy::version = "1.82.0"] + #[clippy::version = "1.84.0"] pub MANUAL_IGNORE_CASE_CMP, perf, "manual case-insensitive ASCII comparison" diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 3f01f3cf30ae..9860deba8432 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -7,9 +7,9 @@ use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operator use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd}; +use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd, PatExpr, PatExprKind, Lit}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; @@ -114,8 +114,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { let arg = peel_ref_operators(cx, arg); - let ty_sugg = get_ty_sugg(cx, arg, start); - let range = check_range(start, end); + let ty_sugg = get_ty_sugg(cx, arg); + let range = check_expr_range(start, end); check_is_ascii(cx, expr.span, arg, &range, ty_sugg); } } @@ -123,19 +123,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { extract_msrv_attr!(LateContext); } -fn get_ty_sugg(cx: &LateContext<'_>, arg: &Expr<'_>, bound_expr: &Expr<'_>) -> Option<(Span, &'static str)> { - if let ExprKind::Lit(lit) = bound_expr.kind - && let local_hid = path_to_local(arg)? - && let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) +fn get_ty_sugg<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'_>) -> Option<(Span, Ty<'tcx>)> { + let local_hid = path_to_local(arg)?; + if let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) // `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given && ty_span == span { - let ty_str = match lit.node { - Char(_) => "char", - Byte(_) => "u8", - _ => return None, - }; - return Some((*ty_span, ty_str)); + let arg_type = cx.typeck_results().expr_ty(arg); + return Some((*ty_span, arg_type)); } None } @@ -145,7 +140,7 @@ fn check_is_ascii( span: Span, recv: &Expr<'_>, range: &CharRange, - ty_sugg: Option<(Span, &'_ str)>, + ty_sugg: Option<(Span, Ty<'_>)>, ) { let sugg = match range { CharRange::UpperChar => "is_ascii_uppercase", @@ -159,8 +154,8 @@ fn check_is_ascii( let mut app = Applicability::MachineApplicable; let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par(); let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))]; - if let Some((ty_span, ty_str)) = ty_sugg { - suggestion.push((ty_span, format!("{recv}: {ty_str}"))); + if let Some((ty_span, ty)) = ty_sugg { + suggestion.push((ty_span, format!("{recv}: {ty}"))); } span_lint_and_then( @@ -196,19 +191,33 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { } } -fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { +fn check_expr_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { if let ExprKind::Lit(start_lit) = &start.kind && let ExprKind::Lit(end_lit) = &end.kind { - match (&start_lit.node, &end_lit.node) { - (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, - (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, - (Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter, - (Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter, - (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, - _ => CharRange::Otherwise, - } + check_lit_range(start_lit, end_lit) } else { CharRange::Otherwise } } + +fn check_range(start: &PatExpr<'_>, end: &PatExpr<'_>) -> CharRange { + if let PatExprKind::Lit{ lit: start_lit, negated: false } = &start.kind + && let PatExprKind::Lit{ lit: end_lit, negated: false } = &end.kind + { + check_lit_range(start_lit, end_lit) + } else { + CharRange::Otherwise + } +} + +fn check_lit_range(start_lit: &Lit, end_lit: &Lit) -> CharRange { + match (&start_lit.node, &end_lit.node) { + (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, + (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, + (Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter, + (Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter, + (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, + _ => CharRange::Otherwise, + } +} diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs index ffa3eacf3544..2e5a92915d9c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs @@ -3,7 +3,7 @@ use clippy_utils::source::SpanRangeExt; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp}; +use rustc_hir::{PatExpr, PatExprKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; @@ -38,14 +38,13 @@ declare_clippy_lint! { } declare_lint_pass!(ManualRangePatterns => [MANUAL_RANGE_PATTERNS]); -fn expr_as_i128(expr: &Expr<'_>) -> Option { - if let ExprKind::Unary(UnOp::Neg, expr) = expr.kind { - expr_as_i128(expr).map(|num| -num) - } else if let ExprKind::Lit(lit) = expr.kind +fn expr_as_i128(expr: &PatExpr<'_>) -> Option { + if let PatExprKind::Lit { lit, negated } = expr.kind && let LitKind::Int(num, _) = lit.node { // Intentionally not handling numbers greater than i128::MAX (for u128 literals) for now. - num.get().try_into().ok() + let n = i128::try_from(num.get()).ok()?; + Some(if negated { -n } else { n }) } else { None } @@ -58,7 +57,7 @@ struct Num { } impl Num { - fn new(expr: &Expr<'_>) -> Option { + fn new(expr: &PatExpr<'_>) -> Option { Some(Self { val: expr_as_i128(expr)?, span: expr.span, @@ -90,7 +89,7 @@ impl LateLintPass<'_> for ManualRangePatterns { let mut ranges_found = Vec::new(); for pat in pats { - if let PatKind::Lit(lit) = pat.kind + if let PatKind::Expr(lit) = pat.kind && let Some(num) = Num::new(lit) { numbers_found.insert(num.val); diff --git a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs index 69105ff0d5c7..7e43d222a662 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs @@ -4,7 +4,7 @@ use clippy_utils::source::{expr_block, snippet}; use clippy_utils::sugg::Sugg; use rustc_ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, PatKind}; +use rustc_hir::{Arm, Expr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -21,8 +21,8 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] move |diag| { if arms.len() == 2 { // no guards - let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { - if let ExprKind::Lit(lit) = arm_bool.kind { + let exprs = if let PatKind::Expr(arm_bool) = arms[0].pat.kind { + if let PatExprKind::Lit { lit, .. } = arm_bool.kind { match lit.node { LitKind::Bool(true) => Some((arms[0].body, arms[1].body)), LitKind::Bool(false) => Some((arms[1].body, arms[0].body)), 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..28e05c273d5c 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 @@ -7,7 +7,7 @@ use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd}; +use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExprKind, PatKind, RangeEnd}; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; @@ -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 = @@ -309,9 +311,9 @@ impl<'a> NormalizedPat<'a> { ); Self::Tuple(None, pats) }, - PatKind::Lit(e) => match &e.kind { + PatKind::Expr(e) => match &e.kind { // TODO: Handle negative integers. They're currently treated as a wild match. - ExprKind::Lit(lit) => match lit.node { + PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes), LitKind::Byte(val) => Self::LitInt(val.into()), @@ -328,7 +330,7 @@ impl<'a> NormalizedPat<'a> { let start = match start { None => 0, Some(e) => match &e.kind { - ExprKind::Lit(lit) => match lit.node { + PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Int(val, _) => val.get(), LitKind::Char(val) => val.into(), LitKind::Byte(val) => val.into(), @@ -340,7 +342,7 @@ impl<'a> NormalizedPat<'a> { let (end, bounds) = match end { None => (u128::MAX, RangeEnd::Included), Some(e) => match &e.kind { - ExprKind::Lit(lit) => match lit.node { + PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Int(val, _) => (val.get(), bounds), LitKind::Char(val) => (val.into(), bounds), LitKind::Byte(val) => (val.into(), bounds), diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs index 1267fc9d0a53..9f5b7c855a13 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; -use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, PatExpr, PatExprKind, LangItem, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; @@ -85,8 +85,8 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<( }; for arm in arms { - if let PatKind::Lit(Expr { - kind: ExprKind::Lit(lit), + if let PatKind::Expr(PatExpr { + kind: PatExprKind::Lit { lit, negated: false }, .. }) = arm.pat.kind && let LitKind::Str(symbol, _) = lit.node diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 1fd2ebcb54a6..ac1eae07eff6 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -583,11 +583,6 @@ declare_clippy_lint! { /// are the same on purpose, you can factor them /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns). /// - /// ### Known problems - /// False positive possible with order dependent `match` - /// (see issue - /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)). - /// /// ### Example /// ```rust,ignore /// match foo { diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 63bea586caf6..0d5575efc220 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -8,7 +8,7 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatKind, Path, QPath}; +use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExprKind, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -133,7 +133,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> }, // compare match_expr ty with RetTy in `fn foo() -> RetTy` Node::Item(item) => { - if let ItemKind::Fn{ .. } = item.kind { + if let ItemKind::Fn { .. } = item.kind { let output = cx .tcx .fn_sig(item.owner_id) @@ -189,8 +189,12 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { }); }, // Example: `5 => 5` - (PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => { - if let ExprKind::Lit(pat_spanned) = &pat_lit_expr.kind { + (PatKind::Expr(pat_expr_expr), ExprKind::Lit(expr_spanned)) => { + if let PatExprKind::Lit { + lit: pat_spanned, + negated: false, + } = &pat_expr_expr.kind + { return pat_spanned.node == expr_spanned.node; } }, diff --git a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs index 856311899f26..4a5d3c516b88 100644 --- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs @@ -34,13 +34,13 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { let lhs_const = if let Some(lhs) = lhs { - ConstEvalCtxt::new(cx).eval(lhs)? + ConstEvalCtxt::new(cx).eval_pat_expr(lhs)? } else { let min_val_const = ty.numeric_min_val(cx.tcx)?; mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? }; let rhs_const = if let Some(rhs) = rhs { - ConstEvalCtxt::new(cx).eval(rhs)? + ConstEvalCtxt::new(cx).eval_pat_expr(rhs)? } else { let max_val_const = ty.numeric_max_val(cx.tcx)?; mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? @@ -57,8 +57,10 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) }); } - if let PatKind::Lit(value) = pat.kind { - let value = ConstEvalCtxt::new(cx).eval_full_int(value)?; + if let PatKind::Expr(value) = pat.kind { + let value = ConstEvalCtxt::new(cx) + .eval_pat_expr(value)? + .int_value(cx.tcx, cx.typeck_results().node_type(pat.hir_id))?; return Some(SpannedRange { span: pat.span, node: (value, EndBound::Included(value)), diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 264458a86ef4..edac97344a03 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,6 +1,6 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, walk_span_to_context}; +use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::{Sugg, make_unop}; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; @@ -9,7 +9,7 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp}; +use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExprKind, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_span::{Span, Symbol, sym}; @@ -74,8 +74,8 @@ fn find_match_true<'tcx>( span: Span, message: &'static str, ) { - if let PatKind::Lit(lit) = pat.kind - && let ExprKind::Lit(lit) = lit.kind + if let PatKind::Expr(lit) = pat.kind + && let PatExprKind::Lit { lit, negated: false } = lit.kind && let LitKind::Bool(pat_is_true) = lit.node { let mut applicability = Applicability::MachineApplicable; @@ -274,7 +274,9 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op ExprKind::AddrOf(_, _, borrowed) => borrowed, _ => op, }; - let mut sugg = format!("{}.{good_method}", snippet(cx, result_expr.span, "_")); + let mut app = Applicability::MachineApplicable; + let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_par(); + let mut sugg = format!("{receiver_sugg}.{good_method}"); if let Some(guard) = maybe_guard { // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying! @@ -307,7 +309,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op format!("redundant pattern matching, consider using `{good_method}`"), "try", sugg, - Applicability::MachineApplicable, + app, ); } } 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..38f876fed802 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -9,7 +9,7 @@ use rustc_arena::DroplessArena; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_pat}; -use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, StmtKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, TyCtxt, TypeckResults, VariantDef}; use rustc_span::{Span, sym}; @@ -114,7 +114,7 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp } let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); - let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind + let (msg, sugg) = if let PatKind::Path(_) | PatKind::Expr(_) = pat.kind && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() @@ -126,8 +126,8 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp // scrutinee derives PartialEq and the pattern is a constant. let pat_ref_count = match pat.kind { // string literals are already a reference. - PatKind::Lit(Expr { - kind: ExprKind::Lit(lit), + PatKind::Expr(PatExpr { + kind: PatExprKind::Lit { lit, negated: false }, .. }) if lit.node.is_str() || lit.node.is_bytestr() => pat_ref_count + 1, _ => pat_ref_count, @@ -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) @@ -380,7 +384,7 @@ impl<'a> PatState<'a> { PatKind::Wild | PatKind::Binding(_, _, _, None) - | PatKind::Lit(_) + | PatKind::Expr(_) | PatKind::Range(..) | PatKind::Path(_) | PatKind::Never diff --git a/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs new file mode 100644 index 000000000000..208172980c9f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs @@ -0,0 +1,41 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::ty::implements_trait; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Instance; +use rustc_span::{Span, sym}; + +use super::DOUBLE_ENDED_ITERATOR_LAST; + +pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Expr<'_>, call_span: Span) { + let typeck = cx.typeck_results(); + + // if the "last" method is that of Iterator + if is_trait_method(cx, expr, sym::Iterator) + // if self implements DoubleEndedIterator + && let Some(deiter_id) = cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator) + && let self_type = cx.typeck_results().expr_ty(self_expr) + && implements_trait(cx, self_type.peel_refs(), deiter_id, &[]) + // resolve the method definition + && let id = typeck.type_dependent_def_id(expr.hir_id).unwrap() + && let args = typeck.node_args(expr.hir_id) + && let Ok(Some(fn_def)) = Instance::try_resolve(cx.tcx, cx.typing_env(), id, args) + // find the provided definition of Iterator::last + && let Some(item) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name.as_str() == "last") + // if the resolved method is the same as the provided definition + && fn_def.def_id() == last_def.def_id + { + span_lint_and_sugg( + cx, + DOUBLE_ENDED_ITERATOR_LAST, + call_span, + "called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator", + "try", + "next_back()".to_string(), + Applicability::MachineApplicable, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs index 07a7a12b1627..f7bb8c1d696d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_trait_method, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -17,10 +17,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_ let mut applicability = Applicability::MachineApplicable; let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability); + let span = expr.span.with_lo(map_span.lo()); + // If the methods are separated with comments, we don't apply suggestion automatically. + if span_contains_comment(cx.tcx.sess.source_map(), span) { + applicability = Applicability::Unspecified; + } span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span.with_lo(map_span.lo()), + span, format!("called `map(..).flatten()` on `{caller_ty_name}`"), format!("try replacing `map` with `{method_to_use}` and remove the `.flatten()`"), format!("{method_to_use}({closure_snippet})"), diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs index 1f204de01da0..053601446573 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; +use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local}; +use rustc_ast::BindingMode; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, Node, PatKind}; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -24,6 +25,16 @@ pub(super) fn check( && is_expr_untyped_identity_function(cx, map_arg) && let Some(sugg_span) = expr.span.trim_start(caller.span) { + // If the result of `.map(identity)` is used as a mutable reference, + // the caller must not be an immutable binding. + if cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr() + && let Some(hir_id) = path_to_local(caller) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + && !matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) + { + return; + } + span_lint_and_sugg( cx, MAP_IDENTITY, diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 810287fa5416..51351f6b7cd1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -14,6 +14,7 @@ mod clone_on_copy; mod clone_on_ref_ptr; mod cloned_instead_of_copied; mod collapsible_str_replace; +mod double_ended_iterator_last; mod drain_collect; mod err_expect; mod expect_fun_call; @@ -4284,6 +4285,32 @@ declare_clippy_lint! { "map of a trivial closure (not dependent on parameter) over a range" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for `Iterator::last` being called on a `DoubleEndedIterator`, which can be replaced + /// with `DoubleEndedIterator::next_back`. + /// + /// ### Why is this bad? + /// + /// `Iterator::last` is implemented by consuming the iterator, which is unnecessary if + /// the iterator is a `DoubleEndedIterator`. Since Rust traits do not allow specialization, + /// `Iterator::last` cannot be optimized for `DoubleEndedIterator`. + /// + /// ### Example + /// ```no_run + /// let last_arg = "echo hello world".split(' ').last(); + /// ``` + /// Use instead: + /// ```no_run + /// let last_arg = "echo hello world".split(' ').next_back(); + /// ``` + #[clippy::version = "1.85.0"] + pub DOUBLE_ENDED_ITERATOR_LAST, + perf, + "using `Iterator::last` on a `DoubleEndedIterator`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4449,6 +4476,7 @@ impl_lint_pass!(Methods => [ MAP_ALL_ANY_IDENTITY, MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, UNNECESSARY_MAP_OR, + DOUBLE_ENDED_ITERATOR_LAST, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4931,6 +4959,7 @@ impl Methods { false, ); } + double_ended_iterator_last::check(cx, expr, recv, call_span); }, ("len", []) => { if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) { diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs index 348f740e7ddf..6993150fb57a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs @@ -7,6 +7,7 @@ use rustc_span::Span; use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths::CHAR_IS_ASCII; use clippy_utils::source::SpanRangeExt; use clippy_utils::{match_def_path, path_to_local_id, peel_blocks}; @@ -77,7 +78,7 @@ fn handle_expr( if revert != is_all && let ExprKind::Path(path) = fn_path.kind && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id() - && match_def_path(cx, fn_def_id, &["core", "char", "methods", "", "is_ascii"]) + && match_def_path(cx, fn_def_id, &CHAR_IS_ASCII) && path_to_local_id(peels_expr_ref(arg), first_param) && let Some(snippet) = before_chars.get_source_text(cx) { diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index ea4984f83adb..2780c3f8af5c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -470,14 +470,14 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>( captured_ids: HirIdSet, ) -> Option> { let mut visitor = IterFunctionVisitor { - uses: Vec::new(), - target: id, - seen_other: false, - cx, - current_mutably_captured_ids: HirIdSet::default(), illegal_mutable_capture_ids: captured_ids, + current_mutably_captured_ids: HirIdSet::default(), + cx, + uses: Vec::new(), hir_id_uses_map: FxHashMap::default(), current_statement_hir_id: None, + seen_other: false, + target: id, }; visitor.visit_block(block); if visitor.seen_other { diff --git a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs index db2b9d4d92fb..82e66a0500a8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs +++ b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs @@ -1,6 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::STDIN; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_local_use_after_expr; @@ -33,7 +34,7 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool { pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() - && match_def_path(cx, recv_adt.did(), &["std", "io", "stdio", "Stdin"]) + && match_def_path(cx, recv_adt.did(), &STDIN) && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind && let Res::Local(local_id) = path.res { diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index c6d4ef5911ee..8a99974394c3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -297,8 +297,8 @@ fn parse_iter_usage<'tcx>( { Some(IterUsage { kind: IterUsageKind::NextTuple, - span: e.span, unwrap_kind: None, + span: e.span, }) } else { None diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs index b5d8972d7aad..c27d1fb4903b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -124,30 +124,30 @@ pub(super) fn check( match lit.node { ast::LitKind::Bool(false) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, Replacement { + method_name: "any", has_args: true, has_generic_return: false, - method_name: "any", }); }, ast::LitKind::Bool(true) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, Replacement { + method_name: "all", has_args: true, has_generic_return: false, - method_name: "all", }); }, ast::LitKind::Int(Pu128(0), _) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, Replacement { + method_name: "sum", has_args: false, has_generic_return: needs_turbofish(cx, expr), - method_name: "sum", }); }, ast::LitKind::Int(Pu128(1), _) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, Replacement { + method_name: "product", has_args: false, has_generic_return: needs_turbofish(cx, expr), - method_name: "product", }); }, _ => (), diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs index 1199d2897610..b7dbebe60a42 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs @@ -7,7 +7,7 @@ use clippy_utils::source::snippet_opt; use clippy_utils::sugg::{Sugg, make_binop}; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; use clippy_utils::visitors::is_local_used; -use clippy_utils::{is_from_proc_macro, path_to_local_id}; +use clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id}; use rustc_ast::LitKind::Bool; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; @@ -96,11 +96,25 @@ pub(super) fn check<'a>( Sugg::hir(cx, non_binding_location, "") ))); - let binop = make_binop(op.node, &Sugg::hir(cx, recv, ".."), &inner_non_binding) - .maybe_par() - .into_string(); + let mut app = Applicability::MachineApplicable; + let binop = make_binop( + op.node, + &Sugg::hir_with_applicability(cx, recv, "..", &mut app), + &inner_non_binding, + ); - (binop, "a standard comparison", Applicability::MaybeIncorrect) + let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { + match parent_expr.kind { + ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_par(), + ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_par(), + _ => binop, + } + } else { + binop + } + .into_string(); + + (sugg, "a standard comparison", app) } else if !def_bool && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite()) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 42107581ab46..964f1603f0e5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -494,7 +494,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) { match node { Node::Stmt(_) => return true, - Node::Block(..) => continue, + Node::Block(..) => {}, Node::Item(item) => { if let ItemKind::Fn { body: body_id, .. } = &item.kind && let output_ty = return_ty(cx, item.owner_id) diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index 121c4326d648..2572e186ce6c 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::qualify_min_const_fn::is_min_const_fn; -use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; +use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, is_in_test, trait_ref_of_method}; use rustc_errors::Applicability; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; @@ -97,6 +97,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, def_id: LocalDefId, ) { + let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); + if is_in_test(cx.tcx, hir_id) { + return; + } + if !self.msrv.meets(msrvs::CONST_IF_MATCH) { return; } @@ -136,8 +141,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { return; } - let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - // Const fns are not allowed as methods in a trait. { let parent = cx.tcx.hir().get_parent_item(hir_id).def_id; diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 1141728640d4..29dcbaa9e62a 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -192,7 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { match it.kind { - hir::ItemKind::Fn{ .. } => { + hir::ItemKind::Fn { .. } => { // ignore main() if it.ident.name == sym::main { let at_root = cx.tcx.local_parent(it.owner_id.def_id) == CRATE_DEF_ID; diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index b18f18d89e56..05aa425de9ed 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { return; } match it.kind { - hir::ItemKind::Fn{ .. } => { + hir::ItemKind::Fn { .. } => { let desc = "a function"; let attrs = cx.tcx.hir().attrs(it.hir_id()); check_missing_inline_attrs(cx, attrs, it.span, desc); diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs index 3c47d0edfdc5..5f7fde30f03f 100644 --- a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -80,7 +81,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod applicability = Applicability::HasPlaceholders; "&'_ mut self".to_string() } else { - format!("&{} mut self", &lifetime.ident.name) + let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability); + format!("&{lt_name} mut self") } }, (Mode::Ref(None), Mutability::Not) => "&self".to_string(), @@ -89,7 +91,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod applicability = Applicability::HasPlaceholders; "&'_ self".to_string() } else { - format!("&{} self", &lifetime.ident.name) + let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability); + format!("&{lt_name} self") } }, (Mode::Value, Mutability::Mut) => "mut self".to_string(), diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs index f69913ddbfd9..7f91e555054d 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -9,7 +9,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::{Rvalue, StatementKind}; @@ -390,7 +390,7 @@ fn replace_types<'tcx>( projection_predicates: &[ProjectionPredicate<'tcx>], args: &mut [GenericArg<'tcx>], ) -> bool { - let mut replaced = BitSet::new_empty(args.len()); + let mut replaced = DenseBitSet::new_empty(args.len()); let mut deque = VecDeque::with_capacity(args.len()); deque.push_back((param_ty, new_ty)); diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs index c48232f99058..05b31fc84b9b 100644 --- a/src/tools/clippy/clippy_lints/src/needless_continue.rs +++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{indent_of, snippet, snippet_block}; -use rustc_ast::ast; +use rustc_ast::{Block, Label, ast}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -11,6 +11,7 @@ declare_clippy_lint! { /// that contain a `continue` statement in either their main blocks or their /// `else`-blocks, when omitting the `else`-block possibly with some /// rearrangement of code can make the code easier to understand. + /// The lint also checks if the last statement in the loop is a `continue` /// /// ### Why is this bad? /// Having explicit `else` blocks for `if` statements @@ -75,6 +76,49 @@ declare_clippy_lint! { /// # break; /// } /// ``` + /// + /// ```rust + /// # use std::io::ErrorKind; + /// + /// fn foo() -> ErrorKind { ErrorKind::NotFound } + /// for _ in 0..10 { + /// match foo() { + /// ErrorKind::NotFound => { + /// eprintln!("not found"); + /// continue + /// } + /// ErrorKind::TimedOut => { + /// eprintln!("timeout"); + /// continue + /// } + /// _ => { + /// eprintln!("other error"); + /// continue + /// } + /// } + /// } + /// ``` + /// Could be rewritten as + /// + /// + /// ```rust + /// # use std::io::ErrorKind; + /// + /// fn foo() -> ErrorKind { ErrorKind::NotFound } + /// for _ in 0..10 { + /// match foo() { + /// ErrorKind::NotFound => { + /// eprintln!("not found"); + /// } + /// ErrorKind::TimedOut => { + /// eprintln!("timeout"); + /// } + /// _ => { + /// eprintln!("other error"); + /// } + /// } + /// } + /// ``` #[clippy::version = "pre 1.29.0"] pub NEEDLESS_CONTINUE, pedantic, @@ -144,7 +188,7 @@ impl EarlyLintPass for NeedlessContinue { /// /// - The expression is a `continue` node. /// - The expression node is a block with the first statement being a `continue`. -fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool { +fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&Label>) -> bool { match else_expr.kind { ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()), @@ -152,7 +196,7 @@ fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) } } -fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool { +fn is_first_block_stmt_continue(block: &Block, label: Option<&Label>) -> bool { block.stmts.first().is_some_and(|stmt| match stmt.kind { ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { if let ast::ExprKind::Continue(ref l) = e.kind { @@ -166,7 +210,7 @@ fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) } /// If the `continue` has a label, check it matches the label of the loop. -fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast::Label>) -> bool { +fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) -> bool { match (loop_label, continue_label) { // `loop { continue; }` or `'a loop { continue; }` (_, None) => true, @@ -181,7 +225,7 @@ fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast:: /// the AST object representing the loop block of `expr`. fn with_loop_block(expr: &ast::Expr, mut func: F) where - F: FnMut(&ast::Block, Option<&ast::Label>), + F: FnMut(&Block, Option<&Label>), { if let ast::ExprKind::While(_, loop_block, label) | ast::ExprKind::ForLoop { @@ -205,7 +249,7 @@ where /// - The `else` expression. fn with_if_expr(stmt: &ast::Stmt, mut func: F) where - F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr), + F: FnMut(&ast::Expr, &ast::Expr, &Block, &ast::Expr), { match stmt.kind { ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { @@ -231,14 +275,14 @@ struct LintData<'a> { /// The condition expression for the above `if`. if_cond: &'a ast::Expr, /// The `then` block of the `if` statement. - if_block: &'a ast::Block, + if_block: &'a Block, /// The `else` block of the `if` statement. /// Note that we only work with `if` exprs that have an `else` branch. else_expr: &'a ast::Expr, /// The 0-based index of the `if` statement in the containing loop block. stmt_idx: usize, /// The statements of the loop block. - loop_block: &'a ast::Block, + loop_block: &'a Block, } const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant"; @@ -329,33 +373,74 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin ) } -fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let ast::ExprKind::Loop(loop_block, loop_label, ..) = &expr.kind - && let Some(last_stmt) = loop_block.stmts.last() - && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind - && let ast::ExprKind::Continue(continue_label) = inner_expr.kind - && compare_labels(loop_label.as_ref(), continue_label.as_ref()) - { - span_lint_and_help( - cx, - NEEDLESS_CONTINUE, - last_stmt.span, - MSG_REDUNDANT_CONTINUE_EXPRESSION, - None, - DROP_CONTINUE_EXPRESSION_MSG, - ); +fn check_last_stmt_in_expr(inner_expr: &ast::Expr, func: &F) +where + F: Fn(Option<&Label>, Span), +{ + match &inner_expr.kind { + ast::ExprKind::Continue(continue_label) => { + func(continue_label.as_ref(), inner_expr.span); + }, + ast::ExprKind::If(_, then_block, else_block) => { + check_last_stmt_in_block(then_block, func); + if let Some(else_block) = else_block { + check_last_stmt_in_expr(else_block, func); + } + }, + ast::ExprKind::Match(_, arms, _) => { + for arm in arms { + if let Some(expr) = &arm.body { + check_last_stmt_in_expr(expr, func); + } + } + }, + ast::ExprKind::Block(b, _) => { + check_last_stmt_in_block(b, func); + }, + _ => {}, } +} + +fn check_last_stmt_in_block(b: &Block, func: &F) +where + F: Fn(Option<&Label>, Span), +{ + if let Some(last_stmt) = b.stmts.last() + && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind + { + check_last_stmt_in_expr(inner_expr, func); + } +} + +fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { with_loop_block(expr, |loop_block, label| { - for (i, stmt) in loop_block.stmts.iter().enumerate() { + let p = |continue_label: Option<&Label>, span: Span| { + if compare_labels(label, continue_label) { + span_lint_and_help( + cx, + NEEDLESS_CONTINUE, + span, + MSG_REDUNDANT_CONTINUE_EXPRESSION, + None, + DROP_CONTINUE_EXPRESSION_MSG, + ); + } + }; + + let stmts = &loop_block.stmts; + for (i, stmt) in stmts.iter().enumerate() { + let mut maybe_emitted_in_if = false; with_if_expr(stmt, |if_expr, cond, then_block, else_expr| { let data = &LintData { - stmt_idx: i, if_expr, if_cond: cond, if_block: then_block, else_expr, + stmt_idx: i, loop_block, }; + + maybe_emitted_in_if = true; if needless_continue_in_else(else_expr, label) { emit_warning( cx, @@ -365,8 +450,14 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { ); } else if is_first_block_stmt_continue(then_block, label) { emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock); + } else { + maybe_emitted_in_if = false; } }); + + if i == stmts.len() - 1 && !maybe_emitted_in_if { + check_last_stmt_in_block(loop_block, &p); + } } }); } @@ -400,7 +491,7 @@ fn erode_from_back(s: &str) -> String { if ret.is_empty() { s.to_string() } else { ret } } -fn span_of_first_expr_in_block(block: &ast::Block) -> Option { +fn span_of_first_expr_in_block(block: &Block) -> Option { block.stmts.first().map(|stmt| stmt.span) } diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 92644456f63d..ccd507580445 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -144,7 +144,7 @@ impl NoEffect { |diag| { for parent in cx.tcx.hir().parent_iter(stmt.hir_id) { if let Node::Item(item) = parent.1 - && let ItemKind::Fn{ .. } = item.kind + && let ItemKind::Fn { .. } = item.kind && let Node::Block(block) = cx.tcx.parent_hir_node(stmt.hir_id) && let [.., final_stmt] = block.stmts && final_stmt.hir_id == stmt.hir_id diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index ebd301d5156a..8409d179b0f5 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -189,6 +189,8 @@ impl<'tcx> NonCopyConst<'tcx> { } fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool { + // No branch that we check (yet) should continue if val isn't a ValTree::Branch + let ty::ValTree::Branch(val) = val else { return false }; match *ty.kind() { // the fact that we have to dig into every structs to search enums // leads us to the point checking `UnsafeCell` directly is the only option. @@ -197,12 +199,13 @@ impl<'tcx> NonCopyConst<'tcx> { // contained value. ty::Adt(def, ..) if def.is_union() => false, ty::Array(ty, _) => val - .unwrap_branch() .iter() .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), ty::Adt(def, args) if def.is_enum() => { - let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap(); - let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().to_u32()); + let Some((&ty::ValTree::Leaf(variant_index), fields)) = val.split_first() else { + return false; + }; + let variant_index = VariantIdx::from_u32(variant_index.to_u32()); fields .iter() .copied() @@ -215,12 +218,10 @@ impl<'tcx> NonCopyConst<'tcx> { .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty)) }, ty::Adt(def, args) => val - .unwrap_branch() .iter() .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))) .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), ty::Tuple(tys) => val - .unwrap_branch() .iter() .zip(tys) .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index 2fee1c72a91b..56c4157d6fe0 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -270,10 +270,10 @@ impl SimilarNamesNameVisitor<'_, '_, '_> { return; } self.0.names.push(ExistingName { - exemptions: get_exemptions(interned_name).unwrap_or(&[]), interned: ident.name, span: ident.span, len: count, + exemptions: get_exemptions(interned_name).unwrap_or(&[]), }); } diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index 65ef56fd2112..0eca788c7874 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -47,6 +47,7 @@ impl ArithmeticSideEffects { Self { allowed_binary, allowed_unary, + const_span: None, disallowed_int_methods: [ sym::saturating_div, sym::wrapping_div, @@ -55,7 +56,6 @@ impl ArithmeticSideEffects { ] .into_iter() .collect(), - const_span: None, expr_span: None, } } diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs index d2529d4d9f85..668f09bbfd58 100644 --- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs @@ -143,11 +143,11 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { self.searcher = Some(PathbufPushSearcher { local_id: id, lhs_is_let: true, - name: name.name, let_ty_span: local.ty.map(|ty| ty.span), - err_span: local.span, init_val: *init_expr, arg: None, + name: name.name, + err_span: local.span, }); } } @@ -165,10 +165,10 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { local_id: id, lhs_is_let: false, let_ty_span: None, - name: name.ident.name, - err_span: expr.span, init_val: *right, arg: None, + name: name.ident.name, + err_span: expr.span, }); } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs index 6a1d40334e75..a27f9b631143 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_else.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs @@ -69,7 +69,6 @@ impl EarlyLintPass for RedundantElse { ExprKind::If(_, next_then, Some(next_els)) => { then = next_then; els = next_els; - continue; }, // else if without else ExprKind::If(..) => return, diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs index 4f46ca3c7150..658d93e634cf 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs @@ -17,9 +17,9 @@ declare_clippy_lint! { /// Checks for redundant redefinitions of local bindings. /// /// ### Why is this bad? - /// Redundant redefinitions of local bindings do not change behavior and are likely to be unintended. + /// Redundant redefinitions of local bindings do not change behavior other than variable's lifetimes and are likely to be unintended. /// - /// Note that although these bindings do not affect your code's meaning, they _may_ affect `rustc`'s stack allocation. + /// These rebindings can be intentional to shorten the lifetimes of variables because they affect when the `Drop` implementation is called. Other than that, they do not affect your code's meaning but they _may_ affect `rustc`'s stack allocation. /// /// ### Example /// ```no_run @@ -41,7 +41,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.73.0"] pub REDUNDANT_LOCALS, - correctness, + suspicious, "redundant redefinition of a local binding" } declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]); diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 6a5bf1b8045d..9443dca154e3 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -24,6 +24,11 @@ declare_clippy_lint! { /// ```ignore /// Regex::new("(") /// ``` + /// + /// Use instead: + /// ```ignore + /// Regex::new("\(") + /// ``` #[clippy::version = "pre 1.29.0"] pub INVALID_REGEX, correctness, @@ -49,6 +54,11 @@ declare_clippy_lint! { /// ```ignore /// Regex::new("^foobar") /// ``` + /// + /// Use instead: + /// ```ignore + /// str::starts_with("foobar") + /// ``` #[clippy::version = "pre 1.29.0"] pub TRIVIAL_REGEX, nursery, @@ -87,7 +97,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.84.0"] pub REGEX_CREATION_IN_LOOPS, perf, "regular expression compilation performed in a loop" diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index c690696aefc1..597bfddecbc5 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -282,9 +282,9 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { } { let mut apa = AuxParamsAttr { - first_bind_ident: ident, first_block_hir_id: self.ap.curr_block_hir_id, first_block_span: self.ap.curr_block_span, + first_bind_ident: ident, first_method_span: { let expr_or_init = expr_or_init(self.cx, expr); if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr_or_init.kind { @@ -395,8 +395,8 @@ impl Default for AuxParamsAttr { counter: 0, has_expensive_expr_after_last_attr: false, first_block_hir_id: HirId::INVALID, - first_bind_ident: Ident::empty(), first_block_span: DUMMY_SP, + first_bind_ident: Ident::empty(), first_method_span: DUMMY_SP, first_stmt_span: DUMMY_SP, last_bind_ident: Ident::empty(), diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index 8c8f11569c8c..d2d693eaa1f3 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ @@ -203,14 +203,18 @@ impl SlowVectorInit { "len", ); - span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| { - diag.span_suggestion( - vec_alloc.allocation_expr.span.source_callsite(), - "consider replacing this with", - format!("vec![0; {len_expr}]"), - Applicability::Unspecified, - ); - }); + let span_to_replace = slow_fill + .span + .with_lo(vec_alloc.allocation_expr.span.source_callsite().lo()); + span_lint_and_sugg( + cx, + SLOW_VECTOR_INITIALIZATION, + span_to_replace, + msg, + "consider replacing this with", + format!("vec![0; {len_expr}]"), + Applicability::Unspecified, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs index 0d85b1b858a4..3834087f7977 100644 --- a/src/tools/clippy/clippy_lints/src/string_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -11,7 +11,7 @@ use clippy_utils::visitors::{Descend, for_each_expr}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind}; +use rustc_hir::{Expr, ExprKind, PatExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; @@ -171,7 +171,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< return ControlFlow::Break(()); } if arm.pat.walk_short(|pat| match pat.kind { - PatKind::Lit(expr) if let ExprKind::Lit(lit) = expr.kind => { + PatKind::Expr(expr) if let PatExprKind::Lit { lit, negated: false } = expr.kind => { if let LitKind::Char(_) = lit.node { set_char_spans.push(lit.span); } diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 78f0b7d121c2..ff1168005123 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -384,9 +384,9 @@ impl<'tcx> IndexBinding<'_, 'tcx> { fn is_used_after_swap(&mut self, idx_ident: Ident) -> bool { let mut v = IndexBindingVisitor { - found_used: false, - suggest_span: self.suggest_span, idx: idx_ident, + suggest_span: self.suggest_span, + found_used: false, }; for stmt in self.block.stmts { diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs index a1d92c3ac71f..82cc5155380e 100644 --- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs +++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::has_repr_attr; +use clippy_utils::{has_repr_attr, is_in_test}; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -37,7 +37,10 @@ declare_lint_pass!(TrailingEmptyArray => [TRAILING_EMPTY_ARRAY]); impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) { + if is_struct_with_trailing_zero_sized_array(cx, item) + && !has_repr_attr(cx, item.hir_id()) + && !is_in_test(cx.tcx, item.hir_id()) + { span_lint_and_help( cx, TRAILING_EMPTY_ARRAY, diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs index 48d65eb15d9a..26323af31228 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -30,17 +30,14 @@ pub(super) fn check<'tcx>( | (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) => { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, (ReducedTy::OrderedFields(Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) if reduced_tys.from_fat_ptr => { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, // ptr <-> ptr @@ -50,7 +47,6 @@ pub(super) fn check<'tcx>( { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, // fat ptr <-> (*size, *size) diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 363aea8be72e..43cce625c641 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -388,8 +388,8 @@ impl<'tcx> LateLintPass<'tcx> for Types { self.check_fn_decl(cx, decl, CheckTyContext { is_in_trait_impl, - is_exported, in_body: matches!(fn_kind, FnKind::Closure), + is_exported, ..CheckTyContext::default() }); } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs index 8165a45bc5ba..9f107fbeec03 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs @@ -47,7 +47,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.84.0"] pub UNNECESSARY_LITERAL_BOUND, pedantic, "detects &str that could be &'static str in function return types" diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 50a97579df77..8923484bb58f 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -92,7 +92,7 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { - if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { + if let Ident(.., None) | Expr(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { // This is a leaf pattern, so cloning is unprofitable. return; } @@ -228,7 +228,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: us // Therefore they are not some form of constructor `C`, // with which a pattern `C(p_0)` may be formed, // which we would want to join with other `C(p_j)`s. - Ident(.., None) | Lit(_) | Wild | Err(_) | Never | Path(..) | Range(..) | Rest | MacCall(_) + Ident(.., None) | Expr(_) | Wild | Err(_) | Never | Path(..) | Range(..) | Rest | MacCall(_) // Skip immutable refs, as grouping them saves few characters, // and almost always requires adding parens (increasing noisiness). // In the case of only two patterns, replacement adds net characters. diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs index c899b1868a6c..d00bd7f2b3db 100644 --- a/src/tools/clippy/clippy_lints/src/unused_async.rs +++ b/src/tools/clippy/clippy_lints/src/unused_async.rs @@ -120,8 +120,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { let mut visitor = AsyncFnVisitor { cx, found_await: false, - async_depth: 0, await_in_async_block: None, + async_depth: 0, }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id); if !visitor.found_await { @@ -129,9 +129,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { // The actual linting happens in `check_crate_post`, once we've found all // uses of local async functions that do require asyncness to pass typeck self.unused_async_fns.push(UnusedAsyncFn { - await_in_async_block: visitor.await_in_async_block, - fn_span: span, def_id, + fn_span: span, + await_in_async_block: visitor.await_in_async_block, }); } } diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index 89bb429e2656..eaa119b045f1 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -245,9 +245,9 @@ impl<'tcx> UnwrappableVariablesVisitor<'_, 'tcx> { let prev_len = self.unwrappables.len(); for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) { let mut delegate = MutationVisitor { - tcx: self.cx.tcx, is_mutated: false, local_id: unwrap_info.local_id, + tcx: self.cx.tcx, }; let vis = ExprUseVisitor::for_clippy(self.cx, cond.hir_id.owner.def_id, &mut delegate); @@ -397,8 +397,8 @@ impl<'tcx> LateLintPass<'tcx> for Unwrap { } let mut v = UnwrappableVariablesVisitor { - cx, unwrappables: Vec::new(), + cx, }; walk_fn(&mut v, kind, decl, body.id(), fn_id); diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index d2970c93f8e9..4dcc8ac7fb0a 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -4,7 +4,7 @@ use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{ self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, - FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, StructTailExpr, TyKind, + FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; @@ -643,6 +643,27 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(expr); } + fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>) { + let kind = |kind| chain!(self, "let PatExprKind::{kind} = {lit}.kind"); + macro_rules! kind { + ($($t:tt)*) => (kind(format_args!($($t)*))); + } + match lit.value.kind { + PatExprKind::Lit { lit, negated } => { + bind!(self, lit); + bind!(self, negated); + kind!("Lit{{ref {lit}, {negated} }}"); + self.lit(lit); + }, + PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"), + PatExprKind::Path(ref qpath) => { + bind!(self, qpath); + kind!("Path(ref {qpath})"); + self.qpath(qpath); + }, + } + } + fn pat(&self, pat: &Binding<&hir::Pat<'_>>) { let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); macro_rules! kind { @@ -712,16 +733,22 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Ref({pat}, Mutability::{muta:?})"); self.pat(pat); }, - PatKind::Lit(lit_expr) => { + PatKind::Guard(pat, cond) => { + bind!(self, pat, cond); + kind!("Guard({pat}, {cond})"); + self.pat(pat); + self.expr(cond); + }, + PatKind::Expr(lit_expr) => { bind!(self, lit_expr); - kind!("Lit({lit_expr})"); - self.expr(lit_expr); + kind!("Expr({lit_expr})"); + self.pat_expr(lit_expr); }, PatKind::Range(start, end, end_kind) => { opt_bind!(self, start, end); kind!("Range({start}, {end}, RangeEnd::{end_kind:?})"); - start.if_some(|e| self.expr(e)); - end.if_some(|e| self.expr(e)); + start.if_some(|e| self.pat_expr(e)); + end.if_some(|e| self.pat_expr(e)); }, PatKind::Slice(start, middle, end) => { bind!(self, start, end); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs index a5fad68eea18..08c178ed229f 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -73,6 +73,7 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { SimplifiedType::Slice, SimplifiedType::Str, SimplifiedType::Bool, + SimplifiedType::Char, ] .iter() .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter()) diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index ef1c46154d29..0730b561bc29 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -8,7 +8,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method}; +use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -132,9 +132,19 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { for (span, lint_opt) in &self.span_to_lint_map { if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt { - let help_msg = format!("you can use {} directly", suggest_slice.desc(),); + let help_msg = format!("you can use {} directly", suggest_slice.desc()); span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| { - diag.span_suggestion(*span, help_msg, snippet, *applicability); + // If the `vec!` macro contains comment, better not make the suggestion machine + // applicable as it would remove them. + let applicability = if *applicability != Applicability::Unspecified + && let source_map = cx.tcx.sess.source_map() + && span_contains_comment(source_map, *span) + { + Applicability::Unspecified + } else { + *applicability + }; + diag.span_suggestion(*span, help_msg, snippet, applicability); }); } } diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs index cbc6885ae5de..d87d554eb074 100644 --- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -166,8 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { local_id: id, init, lhs_is_let: true, - name: name.name, let_ty_span: local.ty.map(|ty| ty.span), + name: name.name, err_span: local.span, found: 0, last_push_expr: init_expr.hir_id, @@ -206,8 +206,8 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { && name.ident.as_str() == "push" { self.searcher = Some(VecPushSearcher { - found: searcher.found + 1, err_span: searcher.err_span.to(stmt.span), + found: searcher.found + 1, last_push_expr: expr.hir_id, ..searcher }); diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index a42ddcdae353..31ae002e47d9 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -248,8 +248,8 @@ impl Write { pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { Self { format_args, - allow_print_in_tests: conf.allow_print_in_tests, in_debug_impl: false, + allow_print_in_tests: conf.allow_print_in_tests, } } } diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs index a702e0785a96..4df34891a2b1 100644 --- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs +++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs @@ -300,8 +300,8 @@ fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, cause: Caus }; let mut vis = ExitPointFinder { - cx, state: ExitPointState::WalkUpTo(spawn_expr.hir_id), + cx, }; if let Break(ExitCallFound) = vis.visit_block(block) { // Visitor found an unconditional `exit()` call, so don't lint. diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 945827c98c17..7fa070cd226b 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_utils" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version edition = "2021" description = "Helpful tools for writing lints, provided as they are used in Clippy" diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 73fefbcd5705..c267b804124a 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2024-12-26 +nightly-2025-01-09 ``` diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 623d9c760861..2eb09bac8d88 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -36,7 +36,7 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool { (Paren(l), _) => eq_pat(l, r), (_, Paren(r)) => eq_pat(l, r), (Wild, Wild) | (Rest, Rest) => true, - (Lit(l), Lit(r)) => eq_expr(l, r), + (Expr(l), Expr(r)) => eq_expr(l, r), (Ident(b1, i1, s1), Ident(b2, i2, s2)) => { b1 == b2 && eq_id(*i1, *i2) && both(s1.as_deref(), s2.as_deref(), eq_pat) }, diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 43ddf06730dd..d46beddf7312 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -4,7 +4,6 @@ //! executable MIR bodies, so we have to do this instead. #![allow(clippy::float_cmp)] -use crate::macros::HirNode; use crate::source::{SpanRangeExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; @@ -13,7 +12,7 @@ use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; +use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp, PatExpr, PatExprKind}; use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; @@ -442,30 +441,48 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } + pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option> { + match &pat_expr.kind { + PatExprKind::Lit { lit, negated } => { + let ty = self.typeck.node_type_opt(pat_expr.hir_id); + let val = lit_to_mir_constant(&lit.node, ty); + if *negated { + self.constant_negate(&val, ty?) + } else { + Some(val) + } + } + PatExprKind::ConstBlock(ConstBlock { body, ..}) => self.expr(self.tcx.hir().body(*body).value), + PatExprKind::Path(qpath) => self.qpath(qpath, pat_expr.hir_id), + } + } + + fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option> { + let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, hir_id).opt_def_id() { + self.tcx.crate_name(def_id.krate) == sym::core + } else { + false + }; + self.fetch_path_and_apply(qpath, hir_id, self.typeck.node_type(hir_id), |self_, result| { + let result = mir_to_const(self_.tcx, result)?; + // If source is already Constant we wouldn't want to override it with CoreConstant + self_.source.set( + if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { + ConstantSource::CoreConstant + } else { + ConstantSource::Constant + }, + ); + Some(result) + }) + } + /// Simple constant folding: Insert an expression, get a constant or none. fn expr(&self, e: &Expr<'_>) -> Option> { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e), - ExprKind::Path(ref qpath) => { - let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, e.hir_id()).opt_def_id() { - self.tcx.crate_name(def_id.krate) == sym::core - } else { - false - }; - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { - let result = mir_to_const(self_.tcx, result)?; - // If source is already Constant we wouldn't want to override it with CoreConstant - self_.source.set( - if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { - ConstantSource::CoreConstant - } else { - ConstantSource::Constant - }, - ); - Some(result) - }) - }, + ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id), ExprKind::Block(block, _) => self.block(block), ExprKind::Lit(lit) => { if is_direct_expn_of(e.span, "cfg").is_some() { diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 4e12577b6df6..60be7e4a4d39 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -196,8 +196,8 @@ impl<'hir> IfOrIfLet<'hir> { if let ExprKind::DropTemps(new_cond) = cond.kind { return Some(Self { cond: new_cond, - r#else, then, + r#else, }); } if let ExprKind::Let(..) = cond.kind { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index ed52c481de12..a1c48d5c36cf 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -9,8 +9,8 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, - Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty, - TyKind, + Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, + TraitBoundModifiers, Ty, TyKind, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::LateContext; @@ -489,6 +489,24 @@ impl HirEqInterExpr<'_, '_, '_> { li.name == ri.name && self.eq_pat(lp, rp) } + fn eq_pat_expr(&mut self, left: &PatExpr<'_>, right: &PatExpr<'_>) -> bool { + match (&left.kind, &right.kind) { + ( + &PatExprKind::Lit { + lit: left, + negated: left_neg, + }, + &PatExprKind::Lit { + lit: right, + negated: right_neg, + }, + ) => left_neg == right_neg && left.node == right.node, + (PatExprKind::ConstBlock(left), PatExprKind::ConstBlock(right)) => self.eq_body(left.body, right.body), + (PatExprKind::Path(left), PatExprKind::Path(right)) => self.eq_qpath(left, right), + (PatExprKind::Lit { .. } | PatExprKind::ConstBlock(..) | PatExprKind::Path(..), _) => false, + } + } + /// Checks whether two patterns are the same. fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { match (&left.kind, &right.kind) { @@ -507,11 +525,11 @@ impl HirEqInterExpr<'_, '_, '_> { eq }, (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), - (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r), + (&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r), (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { - both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_expr(a, b)) - && both(le.as_ref(), re.as_ref(), |a, b| self.eq_expr(a, b)) + both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_expr(a, b)) + && both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b)) && (li == ri) }, (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re), @@ -788,8 +806,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { Self { cx, maybe_typeck_results: cx.maybe_typeck_results(), - path_check: PathCheck::default(), s: FxHasher::default(), + path_check: PathCheck::default(), } } @@ -1073,6 +1091,18 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); } + pub fn hash_pat_expr(&mut self, lit: &PatExpr<'_>) { + std::mem::discriminant(&lit.kind).hash(&mut self.s); + match &lit.kind { + PatExprKind::Lit { lit, negated } => { + lit.node.hash(&mut self.s); + negated.hash(&mut self.s); + }, + PatExprKind::ConstBlock(c) => self.hash_body(c.body), + PatExprKind::Path(qpath) => self.hash_qpath(qpath), + } + } + pub fn hash_pat(&mut self, pat: &Pat<'_>) { std::mem::discriminant(&pat.kind).hash(&mut self.s); match pat.kind { @@ -1084,7 +1114,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat), - PatKind::Lit(expr) => self.hash_expr(expr), + PatKind::Expr(expr) => self.hash_pat_expr(expr), PatKind::Or(pats) => { for pat in pats { self.hash_pat(pat); @@ -1093,10 +1123,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { PatKind::Path(ref qpath) => self.hash_qpath(qpath), PatKind::Range(s, e, i) => { if let Some(s) = s { - self.hash_expr(s); + self.hash_pat_expr(s); } if let Some(e) = e { - self.hash_expr(e); + self.hash_pat_expr(e); } std::mem::discriminant(&i).hash(&mut self.s); }, @@ -1104,6 +1134,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..eecfc3fb13f8 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1243,9 +1243,9 @@ pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<' let mut v = V { cx, - allow_closure: true, loops: Vec::new(), locals: HirIdSet::default(), + allow_closure: true, captures: HirIdMap::default(), }; v.visit_expr(expr); @@ -1777,7 +1777,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, } }, - PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) => true, + PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true, } } diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs index 3924e384c371..ccbbccd0dbff 100644 --- a/src/tools/clippy/clippy_utils/src/mir/mod.rs +++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs @@ -1,5 +1,5 @@ use rustc_hir::{Expr, HirId}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{ BasicBlock, Body, InlineAsmOperand, Local, Location, Place, START_BLOCK, StatementKind, TerminatorKind, traversal, @@ -88,7 +88,7 @@ impl<'tcx> Visitor<'tcx> for V<'_> { /// Checks if the block is part of a cycle pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool { - let mut seen = BitSet::new_empty(body.basic_blocks.len()); + let mut seen = DenseBitSet::new_empty(body.basic_blocks.len()); let mut to_visit = Vec::with_capacity(body.basic_blocks.len() / 2); seen.insert(block); diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs index 17e6558a41c4..5eb9b3b8f227 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs @@ -2,7 +2,7 @@ use super::possible_origin::PossibleOriginVisitor; use super::transitive_relation::TransitiveRelation; use crate::ty::is_copy; use rustc_data_structures::fx::FxHashMap; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_lint::LateContext; use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{self, Mutability}; @@ -21,19 +21,19 @@ struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { possible_borrower: TransitiveRelation, body: &'b mir::Body<'tcx>, cx: &'a LateContext<'tcx>, - possible_origin: FxHashMap>, + possible_origin: FxHashMap>, } impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { fn new( cx: &'a LateContext<'tcx>, body: &'b mir::Body<'tcx>, - possible_origin: FxHashMap>, + possible_origin: FxHashMap>, ) -> Self { Self { possible_borrower: TransitiveRelation::default(), - cx, body, + cx, possible_origin, } } @@ -56,7 +56,7 @@ impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { } } - let bs = BitSet::new_empty(self.body.local_decls.len()); + let bs = DenseBitSet::new_empty(self.body.local_decls.len()); PossibleBorrowerMap { map, maybe_live, @@ -119,7 +119,7 @@ impl<'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'_, '_, 'tcx> { let mut mutable_variables: Vec = mutable_borrowers .iter() .filter_map(|r| self.possible_origin.get(r)) - .flat_map(BitSet::iter) + .flat_map(DenseBitSet::iter) .collect(); if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { @@ -171,10 +171,10 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { #[allow(clippy::module_name_repetitions)] pub struct PossibleBorrowerMap<'b, 'tcx> { /// Mapping `Local -> its possible borrowers` - pub map: FxHashMap>, + pub map: FxHashMap>, maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>, - // Caches to avoid allocation of `BitSet` on every query - pub bitset: (BitSet, BitSet), + // Caches to avoid allocation of `DenseBitSet` on every query + pub bitset: (DenseBitSet, DenseBitSet), } impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { @@ -184,7 +184,7 @@ impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { vis.visit_body(mir); vis.into_map(cx) }; - let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) + let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(DenseBitSet::new_empty(mir.local_decls.len()))) .iterate_to_fixpoint(cx.tcx, mir, Some("redundant_clone")) .into_results_cursor(mir); let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs b/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs index 47b93aad20c8..3d253fd2bb14 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs @@ -1,7 +1,7 @@ use super::transitive_relation::TransitiveRelation; use crate::ty::is_copy; use rustc_data_structures::fx::FxHashMap; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_lint::LateContext; use rustc_middle::mir; @@ -22,7 +22,7 @@ impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { } } - pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { + pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { let mut map = FxHashMap::default(); for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { if is_copy(cx, self.body.local_decls[row].ty) { diff --git a/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs b/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs index 74d1f60af71c..da44829a4c80 100644 --- a/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs +++ b/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs @@ -1,5 +1,5 @@ use rustc_data_structures::fx::FxHashMap; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir; #[derive(Default)] @@ -12,8 +12,8 @@ impl TransitiveRelation { self.relations.entry(a).or_default().push(b); } - pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> BitSet { - let mut seen = BitSet::new_empty(domain_size); + pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> DenseBitSet { + let mut seen = DenseBitSet::new_empty(domain_size); let mut stack = vec![a]; while let Some(u) = stack.pop() { if let Some(edges) = self.relations.get(&u) { diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 98bcedecccc6..2169a5fdd63b 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -130,7 +130,7 @@ impl Msrv { let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym_msrv])); if let Some(msrv_attr) = msrv_attrs.next() { - if let Some(duplicate) = msrv_attrs.last() { + if let Some(duplicate) = msrv_attrs.next_back() { sess.dcx() .struct_span_err(duplicate.span(), "`clippy::msrv` is defined multiple times") .with_span_note(msrv_attr.span(), "first definition found here") diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 8cb8cd590140..f15fffc09e8d 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -33,6 +33,8 @@ pub const CHILD: [&str; 3] = ["std", "process", "Child"]; pub const CHILD_ID: [&str; 4] = ["std", "process", "Child", "id"]; pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"]; pub const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"]; +pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "", "is_ascii"]; +pub const STDIN: [&str; 4] = ["std", "io", "stdio", "Stdin"]; // Paths in clippy itself pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"]; diff --git a/src/tools/clippy/lintcheck/src/output.rs b/src/tools/clippy/lintcheck/src/output.rs index e38036315c2c..dcc1ec339ef9 100644 --- a/src/tools/clippy/lintcheck/src/output.rs +++ b/src/tools/clippy/lintcheck/src/output.rs @@ -94,8 +94,8 @@ impl ClippyWarning { Some(Self { name, diag, - url, krate: krate.to_string(), + url, }) } diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 1000d90f52a5..b1f0a82b1f47 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2024-12-26" +channel = "nightly-2025-01-09" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index b8e0413e97bc..e2e4d92df79f 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -574,12 +574,12 @@ impl LintMetadata { id_location: None, group: "deprecated", level: "none", - version, docs: format!( "### What it does\n\n\ Nothing. This lint has been deprecated\n\n\ ### Deprecation reason\n\n{reason}.\n", ), + version, applicability: Applicability::Unspecified, } } diff --git a/src/tools/clippy/tests/missing-test-files.rs b/src/tools/clippy/tests/missing-test-files.rs index a8225d037e82..64eba5e0888a 100644 --- a/src/tools/clippy/tests/missing-test-files.rs +++ b/src/tools/clippy/tests/missing-test-files.rs @@ -59,7 +59,7 @@ fn explore_directory(dir: &Path) -> Vec { missing_files.push(path.to_str().unwrap().to_string()); } }, - _ => continue, + _ => {}, }; } } diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr index b99e8c0e76f0..ff178924bd15 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr @@ -1,3 +1,4 @@ + thread '' panicked at clippy_lints/src/utils/internal_lints/produce_ice.rs: Would you like some help with that? note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml new file mode 100644 index 000000000000..f43c9d97e825 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml @@ -0,0 +1 @@ +lint-inconsistent-struct-field-initializers = true diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed new file mode 100644 index 000000000000..8092e40ff9f1 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed @@ -0,0 +1,79 @@ +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +fn main() { + let x = 1; + let y = 1; + let z = 1; + + Foo { x, y, z: z }; + + Foo { + x, + z: z, + ..Default::default() + }; +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1859261645 +mod field_attributes { + struct HirId; + struct BodyVisitor { + macro_unsafe_blocks: Vec, + expn_depth: u32, + } + fn check_body(condition: bool) { + BodyVisitor { + macro_unsafe_blocks: Vec::new(), + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if condition { 1 } else { 0 }, + }; + } +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800 +mod cfgs_between_fields { + #[allow(clippy::non_minimal_cfg)] + fn cfg_all() { + struct S { + a: i32, + b: i32, + #[cfg(all())] + c: i32, + d: i32, + } + let s = S { + a: 3, + b: 2, + #[cfg(all())] + c: 1, + d: 0, + }; + } + + fn cfg_any() { + struct S { + a: i32, + b: i32, + #[cfg(any())] + c: i32, + d: i32, + } + let s = S { + a: 3, + #[cfg(any())] + c: 1, + b: 2, + d: 0, + }; + } +} diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs new file mode 100644 index 000000000000..cd1aff966528 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs @@ -0,0 +1,79 @@ +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +fn main() { + let x = 1; + let y = 1; + let z = 1; + + Foo { y, x, z: z }; + + Foo { + z: z, + x, + ..Default::default() + }; +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1859261645 +mod field_attributes { + struct HirId; + struct BodyVisitor { + macro_unsafe_blocks: Vec, + expn_depth: u32, + } + fn check_body(condition: bool) { + BodyVisitor { + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if condition { 1 } else { 0 }, + macro_unsafe_blocks: Vec::new(), + }; + } +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800 +mod cfgs_between_fields { + #[allow(clippy::non_minimal_cfg)] + fn cfg_all() { + struct S { + a: i32, + b: i32, + #[cfg(all())] + c: i32, + d: i32, + } + let s = S { + d: 0, + #[cfg(all())] + c: 1, + b: 2, + a: 3, + }; + } + + fn cfg_any() { + struct S { + a: i32, + b: i32, + #[cfg(any())] + c: i32, + d: i32, + } + let s = S { + d: 0, + #[cfg(any())] + c: 1, + b: 2, + a: 3, + }; + } +} diff --git a/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr new file mode 100644 index 000000000000..d2533960b84c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr @@ -0,0 +1,77 @@ +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:18:11 + | +LL | Foo { y, x, z: z }; + | ^^^^^^^^^^ help: if the field evaluation order doesn't matter, try: `x, y, z: z` + | + = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]` + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:21:9 + | +LL | / z: z, +LL | | x, + | |_________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ x, +LL ~ z: z, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:36:13 + | +LL | / #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning +LL | | expn_depth: if condition { 1 } else { 0 }, +LL | | macro_unsafe_blocks: Vec::new(), + | |___________________________________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ macro_unsafe_blocks: Vec::new(), +LL + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning +LL ~ expn_depth: if condition { 1 } else { 0 }, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:55:13 + | +LL | / d: 0, +LL | | #[cfg(all())] +LL | | c: 1, +LL | | b: 2, +LL | | a: 3, + | |________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ a: 3, +LL + b: 2, +LL + #[cfg(all())] +LL + c: 1, +LL ~ d: 0, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:72:13 + | +LL | / d: 0, +LL | | #[cfg(any())] +LL | | c: 1, +LL | | b: 2, +LL | | a: 3, + | |________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ a: 3, +LL + #[cfg(any())] +LL + c: 1, +LL + b: 2, +LL ~ d: 0, + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 200129da25f5..01e9f5c26a37 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -46,6 +46,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -134,6 +135,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -222,6 +224,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout index a85dcddd3315..8ffdf8862027 100644 --- a/src/tools/clippy/tests/ui/author/if.stdout +++ b/src/tools/clippy/tests/ui/author/if.stdout @@ -30,8 +30,8 @@ if let StmtKind::Let(local) = stmt.kind } if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind && let ExprKind::Let(let_expr) = cond.kind - && let PatKind::Lit(lit_expr) = let_expr.pat.kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatKind::Expr(lit_expr) = let_expr.pat.kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node && let ExprKind::Path(ref qpath) = let_expr.init.kind && match_qpath(qpath, &["a"]) diff --git a/src/tools/clippy/tests/ui/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout index 609d24910610..c94eb171f52b 100644 --- a/src/tools/clippy/tests/ui/author/loop.stdout +++ b/src/tools/clippy/tests/ui/author/loop.stdout @@ -76,8 +76,8 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While: // report your lint here } if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) - && let PatKind::Lit(lit_expr) = let_pat.kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatKind::Expr(lit_expr) = let_pat.kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node && let ExprKind::Path(ref qpath) = let_expr.kind && match_qpath(qpath, &["a"]) diff --git a/src/tools/clippy/tests/ui/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout index 91b3b6f6877e..acb3b140dfa1 100644 --- a/src/tools/clippy/tests/ui/author/matches.stdout +++ b/src/tools/clippy/tests/ui/author/matches.stdout @@ -4,14 +4,14 @@ if let StmtKind::Let(local) = stmt.kind && let ExprKind::Lit(ref lit) = scrutinee.kind && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node && arms.len() == 3 - && let PatKind::Lit(lit_expr) = arms[0].pat.kind - && let ExprKind::Lit(ref lit1) = lit_expr.kind + && let PatKind::Expr(lit_expr) = arms[0].pat.kind + && let PatExprKind::Lit{ref lit1, negated } = lit_expr.kind && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node && arms[0].guard.is_none() && let ExprKind::Lit(ref lit2) = arms[0].body.kind && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node - && let PatKind::Lit(lit_expr1) = arms[1].pat.kind - && let ExprKind::Lit(ref lit3) = lit_expr1.kind + && let PatKind::Expr(lit_expr1) = arms[1].pat.kind + && let PatExprKind::Lit{ref lit3, negated1 } = lit_expr1.kind && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node && arms[1].guard.is_none() && let ExprKind::Block(block, None) = arms[1].body.kind diff --git a/src/tools/clippy/tests/ui/author/struct.stdout b/src/tools/clippy/tests/ui/author/struct.stdout index 0b332d5e7d0e..b66bbccb3cf1 100644 --- a/src/tools/clippy/tests/ui/author/struct.stdout +++ b/src/tools/clippy/tests/ui/author/struct.stdout @@ -23,8 +23,8 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind && match_qpath(qpath, &["Test"]) && fields.len() == 1 && fields[0].ident.as_str() == "field" - && let PatKind::Lit(lit_expr) = fields[0].pat.kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatKind::Expr(lit_expr) = fields[0].pat.kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind @@ -36,8 +36,8 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind && match_qpath(qpath, &["TestTuple"]) && fields.len() == 1 - && let PatKind::Lit(lit_expr) = fields[0].kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatKind::Expr(lit_expr) = fields[0].kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed index 289a5ef38b8d..5365f3dd443c 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed @@ -16,4 +16,12 @@ fn main() { let mut val_mut = 1; let _p_mut = std::ptr::addr_of_mut!(val_mut); + + let mut x: [i32; 2] = [42, 43]; + let _raw = std::ptr::addr_of_mut!(x[1]).wrapping_offset(-1); +} + +fn issue_13882() { + let mut x: [i32; 2] = [42, 43]; + let _raw = (&raw mut x[1]).wrapping_offset(-1); } diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs index b5328cb22dcd..261894f1341c 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.rs +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs @@ -16,4 +16,12 @@ fn main() { let mut val_mut = 1; let _p_mut = &mut val_mut as *mut i32; + + let mut x: [i32; 2] = [42, 43]; + let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); +} + +fn issue_13882() { + let mut x: [i32; 2] = [42, 43]; + let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); } diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr index ea618b06e2c8..4595fa4f2487 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr @@ -13,5 +13,17 @@ error: borrow as raw pointer LL | let _p_mut = &mut val_mut as *mut i32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)` -error: aborting due to 2 previous errors +error: borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:21:16 + | +LL | let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(x[1])` + +error: borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:26:17 + | +LL | let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `&raw mut x[1]` + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs index de220505c3e0..a49d53fbbd38 100644 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs @@ -47,6 +47,17 @@ impl std::ops::Deref for StaticRef { } } +// ICE regression test +mod issue12979 { + use std::cell::UnsafeCell; + + const ATOMIC_TUPLE: (Vec>, ()) = (Vec::new(), ()); + + fn main() { + let _x = &ATOMIC_TUPLE.0; + } +} + // use a tuple to make sure referencing a field behind a pointer isn't linted. const CELL_REF: StaticRef<(UnsafeCell,)> = unsafe { StaticRef::new(std::ptr::null()) }; diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr index 9a9028c86498..4cefcc28008d 100644 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:54:5 + --> tests/ui/borrow_interior_mutable_const/others.rs:65:5 | LL | ATOMIC.store(1, Ordering::SeqCst); | ^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::borrow_interior_mutable_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:55:16 + --> tests/ui/borrow_interior_mutable_const/others.rs:66:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); | ^^^^^^ @@ -20,7 +20,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:58:22 + --> tests/ui/borrow_interior_mutable_const/others.rs:69:22 | LL | let _once_ref = &ONCE_INIT; | ^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let _once_ref = &ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:59:25 + --> tests/ui/borrow_interior_mutable_const/others.rs:70:25 | LL | let _once_ref_2 = &&ONCE_INIT; | ^^^^^^^^^ @@ -36,7 +36,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:60:27 + --> tests/ui/borrow_interior_mutable_const/others.rs:71:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; | ^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:61:26 + --> tests/ui/borrow_interior_mutable_const/others.rs:72:26 | LL | let _once_mut = &mut ONCE_INIT; | ^^^^^^^^^ @@ -52,7 +52,7 @@ LL | let _once_mut = &mut ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:72:14 + --> tests/ui/borrow_interior_mutable_const/others.rs:83:14 | LL | let _ = &ATOMIC_TUPLE; | ^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | let _ = &ATOMIC_TUPLE; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:73:14 + --> tests/ui/borrow_interior_mutable_const/others.rs:84:14 | LL | let _ = &ATOMIC_TUPLE.0; | ^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | let _ = &ATOMIC_TUPLE.0; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:74:19 + --> tests/ui/borrow_interior_mutable_const/others.rs:85:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; | ^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:75:14 + --> tests/ui/borrow_interior_mutable_const/others.rs:86:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; | ^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:76:13 + --> tests/ui/borrow_interior_mutable_const/others.rs:87:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); | ^^^^^^^^^^^^ @@ -92,7 +92,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:81:13 + --> tests/ui/borrow_interior_mutable_const/others.rs:92:13 | LL | let _ = ATOMIC_TUPLE.0[0]; | ^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:86:5 + --> tests/ui/borrow_interior_mutable_const/others.rs:97:5 | LL | CELL.set(2); | ^^^^ @@ -108,7 +108,7 @@ LL | CELL.set(2); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:87:16 + --> tests/ui/borrow_interior_mutable_const/others.rs:98:16 | LL | assert_eq!(CELL.get(), 6); | ^^^^ diff --git a/src/tools/clippy/tests/ui/crashes/ice-10972-tait.rs b/src/tools/clippy/tests/ui/crashes/ice-10972-tait.rs new file mode 100644 index 000000000000..f3ab9cebb7c2 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-10972-tait.rs @@ -0,0 +1,9 @@ +// ICE: #10972 +// asked to assemble constituent types of unexpected type: Binder(Foo, []) +#![feature(type_alias_impl_trait)] + +use std::fmt::Debug; +type Foo = impl Debug; +const FOO2: Foo = 22_u32; + +pub fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-13862.rs b/src/tools/clippy/tests/ui/crashes/ice-13862.rs new file mode 100644 index 000000000000..a5f010054b2f --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-13862.rs @@ -0,0 +1,19 @@ +#![crate_type = "lib"] +#![no_std] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +pub struct S; + +impl Future for S { + type Output = (); + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + todo!() + } +} + +pub fn f() -> S { + S +} diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed b/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed new file mode 100644 index 000000000000..06c48e337537 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed @@ -0,0 +1,53 @@ +#![warn(clippy::double_ended_iterator_last)] + +// Typical case +pub fn last_arg(s: &str) -> Option<&str> { + s.split(' ').next_back() +} + +fn main() { + // General case + struct DeIterator; + impl Iterator for DeIterator { + type Item = (); + fn next(&mut self) -> Option { + Some(()) + } + } + impl DoubleEndedIterator for DeIterator { + fn next_back(&mut self) -> Option { + Some(()) + } + } + let _ = DeIterator.next_back(); + // Should not apply to other methods of Iterator + let _ = DeIterator.count(); + + // Should not apply to simple iterators + struct SimpleIterator; + impl Iterator for SimpleIterator { + type Item = (); + fn next(&mut self) -> Option { + Some(()) + } + } + let _ = SimpleIterator.last(); + + // Should not apply to custom implementations of last() + struct CustomLast; + impl Iterator for CustomLast { + type Item = (); + fn next(&mut self) -> Option { + Some(()) + } + fn last(self) -> Option { + Some(()) + } + } + impl DoubleEndedIterator for CustomLast { + fn next_back(&mut self) -> Option { + Some(()) + } + } + let _ = CustomLast.last(); +} diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.rs b/src/tools/clippy/tests/ui/double_ended_iterator_last.rs new file mode 100644 index 000000000000..9c13b496d117 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.rs @@ -0,0 +1,53 @@ +#![warn(clippy::double_ended_iterator_last)] + +// Typical case +pub fn last_arg(s: &str) -> Option<&str> { + s.split(' ').last() +} + +fn main() { + // General case + struct DeIterator; + impl Iterator for DeIterator { + type Item = (); + fn next(&mut self) -> Option { + Some(()) + } + } + impl DoubleEndedIterator for DeIterator { + fn next_back(&mut self) -> Option { + Some(()) + } + } + let _ = DeIterator.last(); + // Should not apply to other methods of Iterator + let _ = DeIterator.count(); + + // Should not apply to simple iterators + struct SimpleIterator; + impl Iterator for SimpleIterator { + type Item = (); + fn next(&mut self) -> Option { + Some(()) + } + } + let _ = SimpleIterator.last(); + + // Should not apply to custom implementations of last() + struct CustomLast; + impl Iterator for CustomLast { + type Item = (); + fn next(&mut self) -> Option { + Some(()) + } + fn last(self) -> Option { + Some(()) + } + } + impl DoubleEndedIterator for CustomLast { + fn next_back(&mut self) -> Option { + Some(()) + } + } + let _ = CustomLast.last(); +} diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr b/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr new file mode 100644 index 000000000000..b795c18a736e --- /dev/null +++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr @@ -0,0 +1,17 @@ +error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator + --> tests/ui/double_ended_iterator_last.rs:5:18 + | +LL | s.split(' ').last() + | ^^^^^^ help: try: `next_back()` + | + = note: `-D clippy::double-ended-iterator-last` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]` + +error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator + --> tests/ui/double_ended_iterator_last.rs:22:24 + | +LL | let _ = DeIterator.last(); + | ^^^^^^ help: try: `next_back()` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed index 4c324587c96f..67bd3e4d2797 100644 --- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed +++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed @@ -60,7 +60,11 @@ mod with_base { let z = 1; // Should lint. - Foo { x, z, ..Default::default() }; + Foo { + x, + z, + ..Default::default() + }; // Should NOT lint because the order is consistent with the definition. Foo { diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr index 97bb7c789a72..c145eb2a239e 100644 --- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr +++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr @@ -1,21 +1,24 @@ error: struct constructor field order is inconsistent with struct definition field order - --> tests/ui/inconsistent_struct_constructor.rs:36:9 + --> tests/ui/inconsistent_struct_constructor.rs:36:15 | LL | Foo { y, x, z }; - | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }` + | ^^^^^^^ help: try: `x, y, z` | = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]` error: struct constructor field order is inconsistent with struct definition field order - --> tests/ui/inconsistent_struct_constructor.rs:63:9 + --> tests/ui/inconsistent_struct_constructor.rs:64:13 | -LL | / Foo { -LL | | z, +LL | / z, LL | | x, -LL | | ..Default::default() -LL | | }; - | |_________^ help: try: `Foo { x, z, ..Default::default() }` + | |_____________^ + | +help: try + | +LL ~ x, +LL ~ z, + | error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/infinite_iter.rs b/src/tools/clippy/tests/ui/infinite_iter.rs index da95ba04b821..178e300ff5bf 100644 --- a/src/tools/clippy/tests/ui/infinite_iter.rs +++ b/src/tools/clippy/tests/ui/infinite_iter.rs @@ -1,4 +1,4 @@ -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::double_ended_iterator_last)] use std::iter::repeat; fn square_is_lower_64(x: &u32) -> bool { diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed index 7d8a584b0224..d7d3d299349e 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed @@ -1,5 +1,10 @@ #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code, clippy::let_unit_value, clippy::useless_vec)] +#![allow( + dead_code, + clippy::let_unit_value, + clippy::useless_vec, + clippy::double_ended_iterator_last +)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs index 58c374ab8cd1..45e1349febd0 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs @@ -1,5 +1,10 @@ #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code, clippy::let_unit_value, clippy::useless_vec)] +#![allow( + dead_code, + clippy::let_unit_value, + clippy::useless_vec, + clippy::double_ended_iterator_last +)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr index 7a822a79494b..e6680266f107 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr @@ -1,5 +1,5 @@ error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:7:29 + --> tests/ui/iter_overeager_cloned.rs:12:29 | LL | let _: Option = vec.iter().cloned().last(); | ^^^^^^^^^^---------------- @@ -10,7 +10,7 @@ LL | let _: Option = vec.iter().cloned().last(); = help: to override `-D warnings` add `#[allow(clippy::iter_overeager_cloned)]` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:9:29 + --> tests/ui/iter_overeager_cloned.rs:14:29 | LL | let _: Option = vec.iter().chain(vec.iter()).cloned().next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------- @@ -18,7 +18,7 @@ LL | let _: Option = vec.iter().chain(vec.iter()).cloned().next(); | help: try: `.next().cloned()` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:11:20 + --> tests/ui/iter_overeager_cloned.rs:16:20 | LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------- @@ -29,7 +29,7 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:13:21 + --> tests/ui/iter_overeager_cloned.rs:18:21 | LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); | ^^^^^^^^^^----------------- @@ -37,7 +37,7 @@ LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); | help: try: `.take(2).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:15:21 + --> tests/ui/iter_overeager_cloned.rs:20:21 | LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); | ^^^^^^^^^^----------------- @@ -45,7 +45,7 @@ LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); | help: try: `.skip(2).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:17:13 + --> tests/ui/iter_overeager_cloned.rs:22:13 | LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------- @@ -53,7 +53,7 @@ LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); | help: try: `.nth(2).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:19:13 + --> tests/ui/iter_overeager_cloned.rs:24:13 | LL | let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] | _____________^ @@ -69,7 +69,7 @@ LL ~ .flatten().cloned(); | error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:24:13 + --> tests/ui/iter_overeager_cloned.rs:29:13 | LL | let _ = vec.iter().cloned().filter(|x| x.starts_with('2')); | ^^^^^^^^^^---------------------------------------- @@ -77,7 +77,7 @@ LL | let _ = vec.iter().cloned().filter(|x| x.starts_with('2')); | help: try: `.filter(|&x| x.starts_with('2')).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:26:13 + --> tests/ui/iter_overeager_cloned.rs:31:13 | LL | let _ = vec.iter().cloned().find(|x| x == "2"); | ^^^^^^^^^^---------------------------- @@ -85,7 +85,7 @@ LL | let _ = vec.iter().cloned().find(|x| x == "2"); | help: try: `.find(|&x| x == "2").cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:30:17 + --> tests/ui/iter_overeager_cloned.rs:35:17 | LL | let _ = vec.iter().cloned().filter(f); | ^^^^^^^^^^------------------- @@ -93,7 +93,7 @@ LL | let _ = vec.iter().cloned().filter(f); | help: try: `.filter(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:31:17 + --> tests/ui/iter_overeager_cloned.rs:36:17 | LL | let _ = vec.iter().cloned().find(f); | ^^^^^^^^^^----------------- @@ -101,7 +101,7 @@ LL | let _ = vec.iter().cloned().find(f); | help: try: `.find(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:37:17 + --> tests/ui/iter_overeager_cloned.rs:42:17 | LL | let _ = vec.iter().cloned().filter(f); | ^^^^^^^^^^------------------- @@ -109,7 +109,7 @@ LL | let _ = vec.iter().cloned().filter(f); | help: try: `.filter(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:38:17 + --> tests/ui/iter_overeager_cloned.rs:43:17 | LL | let _ = vec.iter().cloned().find(f); | ^^^^^^^^^^----------------- @@ -117,7 +117,7 @@ LL | let _ = vec.iter().cloned().find(f); | help: try: `.find(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:45:9 + --> tests/ui/iter_overeager_cloned.rs:50:9 | LL | iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) | ^^^^------------------------------------------------------- @@ -125,7 +125,7 @@ LL | iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) | help: try: `.filter(move |&(&a, b)| a == 1 && b == &target).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:56:13 + --> tests/ui/iter_overeager_cloned.rs:61:13 | LL | iter.cloned().filter(move |S { a, b }| **a == 1 && b == &target) | ^^^^------------------------------------------------------------ @@ -133,7 +133,7 @@ LL | iter.cloned().filter(move |S { a, b }| **a == 1 && b == &target | help: try: `.filter(move |&S { a, b }| **a == 1 && b == &target).cloned()` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:60:13 + --> tests/ui/iter_overeager_cloned.rs:65:13 | LL | let _ = vec.iter().cloned().map(|x| x.len()); | ^^^^^^^^^^-------------------------- @@ -141,7 +141,7 @@ LL | let _ = vec.iter().cloned().map(|x| x.len()); | help: try: `.map(|x| x.len())` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:65:13 + --> tests/ui/iter_overeager_cloned.rs:70:13 | LL | let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); | ^^^^^^^^^^---------------------------------------------- @@ -149,7 +149,7 @@ LL | let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); | help: try: `.for_each(|x| assert!(!x.is_empty()))` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:67:13 + --> tests/ui/iter_overeager_cloned.rs:72:13 | LL | let _ = vec.iter().cloned().all(|x| x.len() == 1); | ^^^^^^^^^^------------------------------- @@ -157,7 +157,7 @@ LL | let _ = vec.iter().cloned().all(|x| x.len() == 1); | help: try: `.all(|x| x.len() == 1)` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:69:13 + --> tests/ui/iter_overeager_cloned.rs:74:13 | LL | let _ = vec.iter().cloned().any(|x| x.len() == 1); | ^^^^^^^^^^------------------------------- diff --git a/src/tools/clippy/tests/ui/len_zero.fixed b/src/tools/clippy/tests/ui/len_zero.fixed index 27319d9c20e8..c9c476ba4214 100644 --- a/src/tools/clippy/tests/ui/len_zero.fixed +++ b/src/tools/clippy/tests/ui/len_zero.fixed @@ -108,6 +108,8 @@ fn main() { let d2s = DerefToDerefToString {}; println!("{}", (**d2s).is_empty()); + println!("{}", std::borrow::Cow::Borrowed("").is_empty()); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. @@ -226,3 +228,23 @@ fn binop_with_macros() { (!has_is_empty.is_empty()).then(|| println!("This can happen.")); } + +fn no_infinite_recursion() -> bool { + struct S; + + impl Deref for S { + type Target = Self; + fn deref(&self) -> &Self::Target { + self + } + } + + impl PartialEq<&'static str> for S { + fn eq(&self, _other: &&'static str) -> bool { + false + } + } + + // Do not crash while checking if S implements `.is_empty()` + S == "" +} diff --git a/src/tools/clippy/tests/ui/len_zero.rs b/src/tools/clippy/tests/ui/len_zero.rs index 03c05bc6ed7b..610a5448d10e 100644 --- a/src/tools/clippy/tests/ui/len_zero.rs +++ b/src/tools/clippy/tests/ui/len_zero.rs @@ -108,6 +108,8 @@ fn main() { let d2s = DerefToDerefToString {}; println!("{}", &**d2s == ""); + println!("{}", std::borrow::Cow::Borrowed("") == ""); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. @@ -226,3 +228,23 @@ fn binop_with_macros() { (compare_to!(0) < has_is_empty.len()).then(|| println!("This can happen.")); } + +fn no_infinite_recursion() -> bool { + struct S; + + impl Deref for S { + type Target = Self; + fn deref(&self) -> &Self::Target { + self + } + } + + impl PartialEq<&'static str> for S { + fn eq(&self, _other: &&'static str) -> bool { + false + } + } + + // Do not crash while checking if S implements `.is_empty()` + S == "" +} diff --git a/src/tools/clippy/tests/ui/len_zero.stderr b/src/tools/clippy/tests/ui/len_zero.stderr index 5c849a2aca64..8d6b57e4b6d4 100644 --- a/src/tools/clippy/tests/ui/len_zero.stderr +++ b/src/tools/clippy/tests/ui/len_zero.stderr @@ -58,107 +58,113 @@ error: comparison to empty slice LL | println!("{}", &**d2s == ""); | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(**d2s).is_empty()` +error: comparison to empty slice + --> tests/ui/len_zero.rs:111:20 + | +LL | println!("{}", std::borrow::Cow::Borrowed("") == ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `std::borrow::Cow::Borrowed("").is_empty()` + error: length comparison to zero - --> tests/ui/len_zero.rs:124:8 + --> tests/ui/len_zero.rs:126:8 | LL | if has_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:127:8 + --> tests/ui/len_zero.rs:129:8 | LL | if has_is_empty.len() != 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:130:8 + --> tests/ui/len_zero.rs:132:8 | LL | if has_is_empty.len() > 0 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:133:8 + --> tests/ui/len_zero.rs:135:8 | LL | if has_is_empty.len() < 1 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:136:8 + --> tests/ui/len_zero.rs:138:8 | LL | if has_is_empty.len() >= 1 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:147:8 + --> tests/ui/len_zero.rs:149:8 | LL | if 0 == has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:150:8 + --> tests/ui/len_zero.rs:152:8 | LL | if 0 != has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:153:8 + --> tests/ui/len_zero.rs:155:8 | LL | if 0 < has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:156:8 + --> tests/ui/len_zero.rs:158:8 | LL | if 1 <= has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:159:8 + --> tests/ui/len_zero.rs:161:8 | LL | if 1 > has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:173:8 + --> tests/ui/len_zero.rs:175:8 | LL | if with_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:185:6 + --> tests/ui/len_zero.rs:187:6 | LL | (has_is_empty.len() > 0).then(|| println!("This can happen.")); | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:186:6 + --> tests/ui/len_zero.rs:188:6 | LL | (has_is_empty.len() == 0).then(|| println!("Or this!")); | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:190:8 + --> tests/ui/len_zero.rs:192:8 | LL | if b.len() != 0 {} | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:224:8 + --> tests/ui/len_zero.rs:226:8 | LL | if has_is_empty.len() == compare_to!(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:225:8 + --> tests/ui/len_zero.rs:227:8 | LL | if has_is_empty.len() == zero!() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:227:6 + --> tests/ui/len_zero.rs:229:6 | LL | (compare_to!(0) < has_is_empty.len()).then(|| println!("This can happen.")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` -error: aborting due to 26 previous errors +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.fixed b/src/tools/clippy/tests/ui/manual_div_ceil.fixed index e7801f7376aa..1fb1df5b4425 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil.fixed @@ -28,3 +28,25 @@ fn main() { let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; } + +fn issue_13843() { + let x = 3usize; + let _ = 2048_usize.div_ceil(x); + + let x = 5usize; + let _ = 2048usize.div_ceil(x); + + let x = 5usize; + let _ = 2048_usize.div_ceil(x); + + let x = 2048usize; + let _ = x.div_ceil(4); + + let _: u32 = 2048_u32.div_ceil(6); + let _: usize = 2048_usize.div_ceil(6); + let _: u32 = 0x2048_u32.div_ceil(0x6); + + let _ = 2048_u32.div_ceil(6u32); + + let _ = 1_000_000_u32.div_ceil(6u32); +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.rs b/src/tools/clippy/tests/ui/manual_div_ceil.rs index 2de74c7eaa88..4f6d38f0d145 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil.rs @@ -28,3 +28,25 @@ fn main() { let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; } + +fn issue_13843() { + let x = 3usize; + let _ = (2048 + x - 1) / x; + + let x = 5usize; + let _ = (2048usize + x - 1) / x; + + let x = 5usize; + let _ = (2048_usize + x - 1) / x; + + let x = 2048usize; + let _ = (x + 4 - 1) / 4; + + let _: u32 = (2048 + 6 - 1) / 6; + let _: usize = (2048 + 6 - 1) / 6; + let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + + let _ = (2048 + 6u32 - 1) / 6u32; + + let _ = (1_000_000 + 6u32 - 1) / 6u32; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.stderr b/src/tools/clippy/tests/ui/manual_div_ceil.stderr index dc652dff405f..3d87fe8e0409 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil.stderr @@ -31,5 +31,59 @@ error: manually reimplementing `div_ceil` LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` -error: aborting due to 5 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:34:13 + | +LL | let _ = (2048 + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:37:13 + | +LL | let _ = (2048usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:40:13 + | +LL | let _ = (2048_usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:43:13 + | +LL | let _ = (x + 4 - 1) / 4; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:45:18 + | +LL | let _: u32 = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:46:20 + | +LL | let _: usize = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:47:18 + | +LL | let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `0x2048_u32.div_ceil(0x6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:49:13 + | +LL | let _ = (2048 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6u32)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:51:13 + | +LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed index a1d678c66898..f32b78aa14d0 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed @@ -23,3 +23,30 @@ fn main() { let _ = (x + (y - 1)) / z; } + +fn issue_13843() { + let x = 3usize; + let _ = 2048_usize.div_ceil(x); + + let x = 5usize; + let _ = 2048usize.div_ceil(x); + + let x = 5usize; + let _ = 2048_usize.div_ceil(x); + + let x = 2048usize; + let _ = x.div_ceil(4); + + let _ = 2048_i32.div_ceil(4); + + let _: u32 = 2048_u32.div_ceil(6); + let _: usize = 2048_usize.div_ceil(6); + let _: u32 = 0x2048_u32.div_ceil(0x6); + + let _ = 2048_u32.div_ceil(6u32); + + let x = -2; + let _ = (-2048_i32).div_ceil(x); + + let _ = 1_000_000_u32.div_ceil(6u32); +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs index 58cb1dbe34d1..54d89fcbd462 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs @@ -23,3 +23,30 @@ fn main() { let _ = (x + (y - 1)) / z; } + +fn issue_13843() { + let x = 3usize; + let _ = (2048 + x - 1) / x; + + let x = 5usize; + let _ = (2048usize + x - 1) / x; + + let x = 5usize; + let _ = (2048_usize + x - 1) / x; + + let x = 2048usize; + let _ = (x + 4 - 1) / 4; + + let _ = (2048 + 4 - 1) / 4; + + let _: u32 = (2048 + 6 - 1) / 6; + let _: usize = (2048 + 6 - 1) / 6; + let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + + let _ = (2048 + 6u32 - 1) / 6u32; + + let x = -2; + let _ = (-2048 + x - 1) / x; + + let _ = (1_000_000 + 6u32 - 1) / 6u32; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr index 361ef9bd9f42..c5e8c1a687cd 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr @@ -43,5 +43,71 @@ error: manually reimplementing `div_ceil` LL | let _ = (z_u + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `z_u.div_ceil(4)` -error: aborting due to 7 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:29:13 + | +LL | let _ = (2048 + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:32:13 + | +LL | let _ = (2048usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:35:13 + | +LL | let _ = (2048_usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:38:13 + | +LL | let _ = (x + 4 - 1) / 4; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:40:13 + | +LL | let _ = (2048 + 4 - 1) / 4; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_i32.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:42:18 + | +LL | let _: u32 = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:43:20 + | +LL | let _: usize = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:44:18 + | +LL | let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `0x2048_u32.div_ceil(0x6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:46:13 + | +LL | let _ = (2048 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6u32)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:49:13 + | +LL | let _ = (-2048 + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(-2048_i32).div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:51:13 + | +LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed index a72caa3a37ee..179149f697db 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed @@ -82,3 +82,8 @@ fn generics() { take_while(|c: u8| c.is_ascii_uppercase()); take_while(|c: char| c.is_ascii_uppercase()); } + +fn adds_type_reference() { + let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ascii_digit()).collect(); + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); +} diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs index bb6e2a317da1..74f35ce94e84 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs @@ -82,3 +82,8 @@ fn generics() { take_while(|c| (b'A'..=b'Z').contains(&c)); take_while(|c: char| ('A'..='Z').contains(&c)); } + +fn adds_type_reference() { + let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect(); + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); +} diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr index a93ccace28a6..92d93208006a 100644 --- a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr +++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr @@ -173,5 +173,27 @@ error: manual check for common ascii range LL | take_while(|c: char| ('A'..='Z').contains(&c)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_uppercase()` -error: aborting due to 27 previous errors +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:87:63 + | +LL | let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ascii_digit()).collect(); + | ~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ + +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:88:71 + | +LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); + | ~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ + +error: aborting due to 29 previous errors diff --git a/src/tools/clippy/tests/ui/map_flatten.rs b/src/tools/clippy/tests/ui/map_flatten.rs index 76916d465919..eafc8b6e81ca 100644 --- a/src/tools/clippy/tests/ui/map_flatten.rs +++ b/src/tools/clippy/tests/ui/map_flatten.rs @@ -55,6 +55,18 @@ fn long_span() { .collect(); } +#[allow(clippy::useless_vec)] +fn no_suggestion_if_comments_present() { + let vec = vec![vec![1, 2, 3]]; + let _ = vec + .iter() + // a lovely comment explaining the code in very detail + .map(|x| x.iter()) + //~^ ERROR: called `map(..).flatten()` on `Iterator` + // the answer to life, the universe and everything could be here + .flatten(); +} + fn main() { long_span(); } diff --git a/src/tools/clippy/tests/ui/map_flatten.stderr b/src/tools/clippy/tests/ui/map_flatten.stderr index a5837b97617d..34bd174d7dde 100644 --- a/src/tools/clippy/tests/ui/map_flatten.stderr +++ b/src/tools/clippy/tests/ui/map_flatten.stderr @@ -102,5 +102,14 @@ LL + } LL + }) | -error: aborting due to 4 previous errors +error: called `map(..).flatten()` on `Iterator` + --> tests/ui/map_flatten.rs:64:10 + | +LL | .map(|x| x.iter()) + | __________^ +... | +LL | | .flatten(); + | |__________________^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| x.iter())` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/map_identity.fixed b/src/tools/clippy/tests/ui/map_identity.fixed index 53ebfb40ba0d..3257ddc6f72b 100644 --- a/src/tools/clippy/tests/ui/map_identity.fixed +++ b/src/tools/clippy/tests/ui/map_identity.fixed @@ -61,3 +61,18 @@ fn issue11764() { // no match ergonomics for `(i32, i32)` let _ = x.iter().copied(); } + +fn issue13904() { + // don't lint: `it.next()` would not be legal as `it` is immutable + let it = [1, 2, 3].into_iter(); + let _ = it.map(|x| x).next(); + + // lint + #[allow(unused_mut)] + let mut it = [1, 2, 3].into_iter(); + let _ = it.next(); + + // lint + let it = [1, 2, 3].into_iter(); + let _ = { it }.next(); +} diff --git a/src/tools/clippy/tests/ui/map_identity.rs b/src/tools/clippy/tests/ui/map_identity.rs index c646c0568595..be3bb9a4f106 100644 --- a/src/tools/clippy/tests/ui/map_identity.rs +++ b/src/tools/clippy/tests/ui/map_identity.rs @@ -65,3 +65,18 @@ fn issue11764() { // no match ergonomics for `(i32, i32)` let _ = x.iter().copied().map(|(x, y)| (x, y)); } + +fn issue13904() { + // don't lint: `it.next()` would not be legal as `it` is immutable + let it = [1, 2, 3].into_iter(); + let _ = it.map(|x| x).next(); + + // lint + #[allow(unused_mut)] + let mut it = [1, 2, 3].into_iter(); + let _ = it.map(|x| x).next(); + + // lint + let it = [1, 2, 3].into_iter(); + let _ = { it }.map(|x| x).next(); +} diff --git a/src/tools/clippy/tests/ui/map_identity.stderr b/src/tools/clippy/tests/ui/map_identity.stderr index 0a0dc9c8f075..aa3fc4ae0b5c 100644 --- a/src/tools/clippy/tests/ui/map_identity.stderr +++ b/src/tools/clippy/tests/ui/map_identity.stderr @@ -73,5 +73,17 @@ error: unnecessary map of the identity function LL | let _ = x.iter().copied().map(|(x, y)| (x, y)); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` -error: aborting due to 11 previous errors +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:77:15 + | +LL | let _ = it.map(|x| x).next(); + | ^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:81:19 + | +LL | let _ = { it }.map(|x| x).next(); + | ^^^^^^^^^^^ help: remove the call to `map` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs index ca323dcf1733..d2f9e34a5ceb 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -47,7 +47,34 @@ fn get_y() -> u32 { Y } -// Don't lint entrypoint functions +#[cfg(test)] +mod with_test_fn { + #[derive(Clone, Copy)] + pub struct Foo { + pub n: u32, + } + + impl Foo { + #[must_use] + pub const fn new(n: u32) -> Foo { + Foo { n } + } + } + + #[test] + fn foo_is_copy() { + let foo = Foo::new(42); + let one = foo; + let two = foo; + _ = one; + _ = two; + } +} + +// Allowing on this function, because it would lint, which we don't want in this case. +// if we have `#[start]` and `#[test]` check `is_entrypoint_fn(cx, def_id.to_def_id())` is stopped +// working +#[allow(clippy::missing_const_for_fn)] #[start] fn init(num: isize, something: *const *const u8) -> isize { 1 diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed index 9da60c687d44..530eb77d83d2 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed @@ -64,4 +64,9 @@ impl ValType { } } +trait Foo<'r#struct> { + fn f1(&'r#struct self) {} + fn f2(&'r#struct mut self) {} +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs index fc4ec5cb0b3c..5a1ff96a11cc 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs @@ -64,4 +64,9 @@ impl ValType { } } +trait Foo<'r#struct> { + fn f1(self: &'r#struct Self) {} + fn f2(self: &'r#struct mut Self) {} +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr index c653267f7525..7ebbbaa122f5 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr @@ -37,5 +37,17 @@ error: the type of the `self` parameter does not need to be arbitrary LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` -error: aborting due to 6 previous errors +error: the type of the `self` parameter does not need to be arbitrary + --> tests/ui/needless_arbitrary_self_type.rs:68:11 + | +LL | fn f1(self: &'r#struct Self) {} + | ^^^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'r#struct self` + +error: the type of the `self` parameter does not need to be arbitrary + --> tests/ui/needless_arbitrary_self_type.rs:69:11 + | +LL | fn f2(self: &'r#struct mut Self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'r#struct mut self` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/needless_continue.rs b/src/tools/clippy/tests/ui/needless_continue.rs index b6d8a8f61aeb..334a2b32775f 100644 --- a/src/tools/clippy/tests/ui/needless_continue.rs +++ b/src/tools/clippy/tests/ui/needless_continue.rs @@ -87,6 +87,14 @@ fn simple_loop4() { } } +fn simple_loop5() { + loop { + println!("bleh"); + { continue } + //~^ ERROR: this `continue` expression is redundant + } +} + mod issue_2329 { fn condition() -> bool { unimplemented!() @@ -168,3 +176,60 @@ fn issue_13641() { } } } + +mod issue_4077 { + fn main() { + 'outer: loop { + 'inner: loop { + do_something(); + if some_expr() { + println!("bar-7"); + continue 'outer; + } else if !some_expr() { + println!("bar-8"); + continue 'inner; + } else { + println!("bar-9"); + continue 'inner; + } + } + } + + for _ in 0..10 { + match "foo".parse::() { + Ok(_) => do_something(), + Err(_) => { + println!("bar-10"); + continue; + }, + } + } + + loop { + if true { + } else { + // redundant `else` + continue; // redundant `continue` + } + } + + loop { + if some_expr() { + continue; + } else { + do_something(); + } + } + } + + // The contents of these functions are irrelevant, the purpose of this file is + // shown in main. + + fn do_something() { + std::process::exit(0); + } + + fn some_expr() -> bool { + true + } +} diff --git a/src/tools/clippy/tests/ui/needless_continue.stderr b/src/tools/clippy/tests/ui/needless_continue.stderr index 0741ba692487..ec39d6234195 100644 --- a/src/tools/clippy/tests/ui/needless_continue.stderr +++ b/src/tools/clippy/tests/ui/needless_continue.stderr @@ -63,7 +63,7 @@ error: this `continue` expression is redundant --> tests/ui/needless_continue.rs:60:9 | LL | continue; - | ^^^^^^^^^ + | ^^^^^^^^ | = help: consider dropping the `continue` expression @@ -71,7 +71,7 @@ error: this `continue` expression is redundant --> tests/ui/needless_continue.rs:68:9 | LL | continue; - | ^^^^^^^^^ + | ^^^^^^^^ | = help: consider dropping the `continue` expression @@ -91,8 +91,16 @@ LL | continue | = help: consider dropping the `continue` expression +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:93:11 + | +LL | { continue } + | ^^^^^^^^ + | + = help: consider dropping the `continue` expression + error: this `else` block is redundant - --> tests/ui/needless_continue.rs:136:24 + --> tests/ui/needless_continue.rs:144:24 | LL | } else { | ________________________^ @@ -117,7 +125,7 @@ LL | | } } error: there is no need for an explicit `else` block for this `if` expression - --> tests/ui/needless_continue.rs:143:17 + --> tests/ui/needless_continue.rs:151:17 | LL | / if condition() { LL | | @@ -137,12 +145,70 @@ LL | | } } error: this `continue` expression is redundant - --> tests/ui/needless_continue.rs:166:13 + --> tests/ui/needless_continue.rs:174:13 | LL | continue 'b; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^ | = help: consider dropping the `continue` expression -error: aborting due to 9 previous errors +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:190:21 + | +LL | continue 'inner; + | ^^^^^^^^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:193:21 + | +LL | continue 'inner; + | ^^^^^^^^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:203:21 + | +LL | continue; + | ^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: this `else` block is redundant + --> tests/ui/needless_continue.rs:210:20 + | +LL | } else { + | ____________________^ +LL | | // redundant `else` +LL | | continue; // redundant `continue` +LL | | } + | |_____________^ + | + = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block + if true { + // merged code follows: + + } + +error: there is no need for an explicit `else` block for this `if` expression + --> tests/ui/needless_continue.rs:217:13 + | +LL | / if some_expr() { +LL | | continue; +LL | | } else { +LL | | do_something(); +LL | | } + | |_____________^ + | + = help: consider dropping the `else` clause + if some_expr() { + continue; + } + { + do_something(); + } + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed index c9b76262d70b..c7e0cd2610f0 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed @@ -137,3 +137,11 @@ fn issue10803() { // Don't lint let _ = matches!(x, Some(16)); } + +fn issue13902() { + let x = Some(0); + let p = &raw const x; + unsafe { + let _ = (*p).is_none(); + } +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs index a5f9caf659c6..6d9a9f7f9428 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs @@ -164,3 +164,11 @@ fn issue10803() { // Don't lint let _ = matches!(x, Some(16)); } + +fn issue13902() { + let x = Some(0); + let p = &raw const x; + unsafe { + let _ = matches!(*p, None); + } +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr index 575f199be42c..34d80f5ca782 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr @@ -209,5 +209,11 @@ error: redundant pattern matching, consider using `is_none()` LL | let _ = matches!(x, None); | ^^^^^^^^^^^^^^^^^ help: try: `x.is_none()` -error: aborting due to 30 previous errors +error: redundant pattern matching, consider using `is_none()` + --> tests/ui/redundant_pattern_matching_option.rs:172:17 + | +LL | let _ = matches!(*p, None); + | ^^^^^^^^^^^^^^^^^^ help: try: `(*p).is_none()` + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.rs b/src/tools/clippy/tests/ui/slow_vector_initialization.rs index 16f81019574f..2ba87f412500 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.rs +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.rs @@ -11,22 +11,22 @@ fn extend_vector() { // Extend with constant expression let len = 300; let mut vec1 = Vec::with_capacity(len); - vec1.extend(repeat(0).take(len)); //~^ ERROR: slow zero-filling initialization //~| NOTE: `-D clippy::slow-vector-initialization` implied by `-D warnings` + vec1.extend(repeat(0).take(len)); // Extend with len expression let mut vec2 = Vec::with_capacity(len - 10); - vec2.extend(repeat(0).take(len - 10)); //~^ ERROR: slow zero-filling initialization + vec2.extend(repeat(0).take(len - 10)); // Extend with mismatching expression should not be warned let mut vec3 = Vec::with_capacity(24322); vec3.extend(repeat(0).take(2)); let mut vec4 = Vec::with_capacity(len); - vec4.extend(repeat(0).take(vec4.capacity())); //~^ ERROR: slow zero-filling initialization + vec4.extend(repeat(0).take(vec4.capacity())); } fn mixed_extend_resize_vector() { @@ -36,20 +36,20 @@ fn mixed_extend_resize_vector() { // Slow initialization let mut resized_vec = Vec::with_capacity(30); - resized_vec.resize(30, 0); //~^ ERROR: slow zero-filling initialization + resized_vec.resize(30, 0); let mut extend_vec = Vec::with_capacity(30); - extend_vec.extend(repeat(0).take(30)); //~^ ERROR: slow zero-filling initialization + extend_vec.extend(repeat(0).take(30)); } fn resize_vector() { // Resize with constant expression let len = 300; let mut vec1 = Vec::with_capacity(len); - vec1.resize(len, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(len, 0); // Resize mismatch len let mut vec2 = Vec::with_capacity(200); @@ -57,39 +57,39 @@ fn resize_vector() { // Resize with len expression let mut vec3 = Vec::with_capacity(len - 10); - vec3.resize(len - 10, 0); //~^ ERROR: slow zero-filling initialization + vec3.resize(len - 10, 0); let mut vec4 = Vec::with_capacity(len); - vec4.resize(vec4.capacity(), 0); //~^ ERROR: slow zero-filling initialization + vec4.resize(vec4.capacity(), 0); // Reinitialization should be warned vec1 = Vec::with_capacity(10); - vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(10, 0); } fn from_empty_vec() { // Resize with constant expression let len = 300; let mut vec1 = Vec::new(); - vec1.resize(len, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(len, 0); // Resize with len expression let mut vec3 = Vec::new(); - vec3.resize(len - 10, 0); //~^ ERROR: slow zero-filling initialization + vec3.resize(len - 10, 0); // Reinitialization should be warned vec1 = Vec::new(); - vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(10, 0); vec1 = vec![]; - vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(10, 0); macro_rules! x { () => { diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr index 353c677097be..7f4b9f7b67a4 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr @@ -1,109 +1,122 @@ error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:14:5 + --> tests/ui/slow_vector_initialization.rs:13:20 | -LL | let mut vec1 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec1.extend(repeat(0).take(len)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec1 = Vec::with_capacity(len); + | ____________________^ +... | +LL | | vec1.extend(repeat(0).take(len)); + | |____________________________________^ help: consider replacing this with: `vec![0; len]` | = note: `-D clippy::slow-vector-initialization` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::slow_vector_initialization)]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:20:5 + --> tests/ui/slow_vector_initialization.rs:19:20 | -LL | let mut vec2 = Vec::with_capacity(len - 10); - | ---------------------------- help: consider replacing this with: `vec![0; len - 10]` -LL | vec2.extend(repeat(0).take(len - 10)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec2 = Vec::with_capacity(len - 10); + | ____________________^ +LL | | +LL | | vec2.extend(repeat(0).take(len - 10)); + | |_________________________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:28:5 + --> tests/ui/slow_vector_initialization.rs:27:20 | -LL | let mut vec4 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec4.extend(repeat(0).take(vec4.capacity())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec4 = Vec::with_capacity(len); + | ____________________^ +LL | | +LL | | vec4.extend(repeat(0).take(vec4.capacity())); + | |________________________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:39:5 + --> tests/ui/slow_vector_initialization.rs:38:27 | -LL | let mut resized_vec = Vec::with_capacity(30); - | ---------------------- help: consider replacing this with: `vec![0; 30]` -LL | resized_vec.resize(30, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut resized_vec = Vec::with_capacity(30); + | ___________________________^ +LL | | +LL | | resized_vec.resize(30, 0); + | |_____________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:43:5 + --> tests/ui/slow_vector_initialization.rs:42:26 | -LL | let mut extend_vec = Vec::with_capacity(30); - | ---------------------- help: consider replacing this with: `vec![0; 30]` -LL | extend_vec.extend(repeat(0).take(30)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut extend_vec = Vec::with_capacity(30); + | __________________________^ +LL | | +LL | | extend_vec.extend(repeat(0).take(30)); + | |_________________________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:51:5 + --> tests/ui/slow_vector_initialization.rs:50:20 | -LL | let mut vec1 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec1.resize(len, 0); - | ^^^^^^^^^^^^^^^^^^^ +LL | let mut vec1 = Vec::with_capacity(len); + | ____________________^ +LL | | +LL | | vec1.resize(len, 0); + | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:60:5 + --> tests/ui/slow_vector_initialization.rs:59:20 | -LL | let mut vec3 = Vec::with_capacity(len - 10); - | ---------------------------- help: consider replacing this with: `vec![0; len - 10]` -LL | vec3.resize(len - 10, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec3 = Vec::with_capacity(len - 10); + | ____________________^ +LL | | +LL | | vec3.resize(len - 10, 0); + | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:64:5 + --> tests/ui/slow_vector_initialization.rs:63:20 | -LL | let mut vec4 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec4.resize(vec4.capacity(), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec4 = Vec::with_capacity(len); + | ____________________^ +LL | | +LL | | vec4.resize(vec4.capacity(), 0); + | |___________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:69:5 + --> tests/ui/slow_vector_initialization.rs:68:12 | -LL | vec1 = Vec::with_capacity(10); - | ---------------------- help: consider replacing this with: `vec![0; 10]` -LL | vec1.resize(10, 0); - | ^^^^^^^^^^^^^^^^^^ +LL | vec1 = Vec::with_capacity(10); + | ____________^ +LL | | +LL | | vec1.resize(10, 0); + | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:77:5 + --> tests/ui/slow_vector_initialization.rs:76:20 | -LL | let mut vec1 = Vec::new(); - | ---------- help: consider replacing this with: `vec![0; len]` -LL | vec1.resize(len, 0); - | ^^^^^^^^^^^^^^^^^^^ +LL | let mut vec1 = Vec::new(); + | ____________________^ +LL | | +LL | | vec1.resize(len, 0); + | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:82:5 + --> tests/ui/slow_vector_initialization.rs:81:20 | -LL | let mut vec3 = Vec::new(); - | ---------- help: consider replacing this with: `vec![0; len - 10]` -LL | vec3.resize(len - 10, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec3 = Vec::new(); + | ____________________^ +LL | | +LL | | vec3.resize(len - 10, 0); + | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:87:5 + --> tests/ui/slow_vector_initialization.rs:86:12 | -LL | vec1 = Vec::new(); - | ---------- help: consider replacing this with: `vec![0; 10]` -LL | vec1.resize(10, 0); - | ^^^^^^^^^^^^^^^^^^ +LL | vec1 = Vec::new(); + | ____________^ +LL | | +LL | | vec1.resize(10, 0); + | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:91:5 + --> tests/ui/slow_vector_initialization.rs:90:12 | -LL | vec1 = vec![]; - | ------ help: consider replacing this with: `vec![0; 10]` -LL | vec1.resize(10, 0); - | ^^^^^^^^^^^^^^^^^^ +LL | vec1 = vec![]; + | ____________^ +LL | | +LL | | vec1.resize(10, 0); + | |______________________^ help: consider replacing this with: `vec![0; 10]` error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/starts_ends_with.fixed b/src/tools/clippy/tests/ui/starts_ends_with.fixed index 4a66ca7ec91a..252b6e5a98c0 100644 --- a/src/tools/clippy/tests/ui/starts_ends_with.fixed +++ b/src/tools/clippy/tests/ui/starts_ends_with.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if, dead_code, unused_must_use)] +#![allow(clippy::needless_if, dead_code, unused_must_use, clippy::double_ended_iterator_last)] fn main() {} diff --git a/src/tools/clippy/tests/ui/starts_ends_with.rs b/src/tools/clippy/tests/ui/starts_ends_with.rs index 16a68e02d66d..6c5655f31782 100644 --- a/src/tools/clippy/tests/ui/starts_ends_with.rs +++ b/src/tools/clippy/tests/ui/starts_ends_with.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if, dead_code, unused_must_use)] +#![allow(clippy::needless_if, dead_code, unused_must_use, clippy::double_ended_iterator_last)] fn main() {} diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.rs b/src/tools/clippy/tests/ui/trailing_empty_array.rs index 309a5920dfde..ea3b8ff01afa 100644 --- a/src/tools/clippy/tests/ui/trailing_empty_array.rs +++ b/src/tools/clippy/tests/ui/trailing_empty_array.rs @@ -193,3 +193,17 @@ type C = ConstParamNoDefault<0>; type D = ConstParamNonZeroDefault<0>; fn main() {} + +#[cfg(test)] +mod tests { + pub struct Friend { + age: u8, + } + + #[test] + fn oldest_empty_is_none() { + struct Michael { + friends: [Friend; 0], + } + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed index 70b78ceca502..efea28e7045c 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed @@ -3,15 +3,16 @@ #![allow(clippy::no_effect)] #![allow(clippy::eq_op)] #![allow(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::nonminimal_bool)] #[clippy::msrv = "1.70.0"] #[macro_use] extern crate proc_macros; fn main() { // should trigger - let _ = (Some(5) == Some(5)); - let _ = (Some(5) != Some(5)); - let _ = (Some(5) == Some(5)); + let _ = Some(5) == Some(5); + let _ = Some(5) != Some(5); + let _ = Some(5) == Some(5); let _ = Some(5).is_some_and(|n| { let _ = n; 6 >= 5 @@ -21,10 +22,13 @@ fn main() { let _ = Some(5).is_some_and(|n| n == n); let _ = Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 }); let _ = Ok::, i32>(vec![5]).is_ok_and(|n| n == [5]); - let _ = (Ok::(5) == Ok(5)); + let _ = Ok::(5) == Ok(5); let _ = (Some(5) == Some(5)).then(|| 1); let _ = Some(5).is_none_or(|n| n == 5); let _ = Some(5).is_none_or(|n| 5 == n); + let _ = !(Some(5) == Some(5)); + let _ = (Some(5) == Some(5)) || false; + let _ = (Some(5) == Some(5)) as usize; macro_rules! x { () => { @@ -60,7 +64,7 @@ fn main() { #[derive(PartialEq)] struct S2; let r: Result = Ok(4); - let _ = (r == Ok(8)); + let _ = r == Ok(8); // do not lint `Result::map_or(true, …)` let r: Result = Ok(4); diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.rs b/src/tools/clippy/tests/ui/unnecessary_map_or.rs index 507577159771..05a0ca816ef6 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.rs +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.rs @@ -3,6 +3,7 @@ #![allow(clippy::no_effect)] #![allow(clippy::eq_op)] #![allow(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::nonminimal_bool)] #[clippy::msrv = "1.70.0"] #[macro_use] extern crate proc_macros; @@ -28,6 +29,9 @@ fn main() { let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); let _ = Some(5).map_or(true, |n| n == 5); let _ = Some(5).map_or(true, |n| 5 == n); + let _ = !Some(5).map_or(false, |n| n == 5); + let _ = Some(5).map_or(false, |n| n == 5) || false; + let _ = Some(5).map_or(false, |n| n == 5) as usize; macro_rules! x { () => { diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr index 890abb012288..2b78996d5f3e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr @@ -1,30 +1,30 @@ error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:12:13 + --> tests/ui/unnecessary_map_or.rs:13:13 | LL | let _ = Some(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) == Some(5)` | = note: `-D clippy::unnecessary-map-or` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_or)]` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:13:13 + --> tests/ui/unnecessary_map_or.rs:14:13 | LL | let _ = Some(5).map_or(true, |n| n != 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) != Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) != Some(5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:14:13 + --> tests/ui/unnecessary_map_or.rs:15:13 | LL | let _ = Some(5).map_or(false, |n| { | _____________^ LL | | let _ = 1; LL | | n == 5 LL | | }); - | |______^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | |______^ help: use a standard comparison instead: `Some(5) == Some(5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:18:13 + --> tests/ui/unnecessary_map_or.rs:19:13 | LL | let _ = Some(5).map_or(false, |n| { | _____________^ @@ -42,88 +42,106 @@ LL ~ }); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:22:13 + --> tests/ui/unnecessary_map_or.rs:23:13 | LL | let _ = Some(vec![5]).map_or(false, |n| n == [5]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![5]).is_some_and(|n| n == [5])` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:23:13 + --> tests/ui/unnecessary_map_or.rs:24:13 | LL | let _ = Some(vec![1]).map_or(false, |n| vec![2] == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![1]).is_some_and(|n| vec![2] == n)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:24:13 + --> tests/ui/unnecessary_map_or.rs:25:13 | LL | let _ = Some(5).map_or(false, |n| n == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == n)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:25:13 + --> tests/ui/unnecessary_map_or.rs:26:13 | LL | let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 })` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:26:13 + --> tests/ui/unnecessary_map_or.rs:27:13 | LL | let _ = Ok::, i32>(vec![5]).map_or(false, |n| n == [5]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `Ok::, i32>(vec![5]).is_ok_and(|n| n == [5])` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:27:13 + --> tests/ui/unnecessary_map_or.rs:28:13 | LL | let _ = Ok::(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Ok::(5) == Ok(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Ok::(5) == Ok(5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:28:13 + --> tests/ui/unnecessary_map_or.rs:29:13 | LL | let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:29:13 + --> tests/ui/unnecessary_map_or.rs:30:13 | LL | let _ = Some(5).map_or(true, |n| n == 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| n == 5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:30:13 + --> tests/ui/unnecessary_map_or.rs:31:13 | LL | let _ = Some(5).map_or(true, |n| 5 == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| 5 == n)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:54:13 + --> tests/ui/unnecessary_map_or.rs:32:14 + | +LL | let _ = !Some(5).map_or(false, |n| n == 5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:33:13 + | +LL | let _ = Some(5).map_or(false, |n| n == 5) || false; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:34:13 + | +LL | let _ = Some(5).map_or(false, |n| n == 5) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:58:13 | LL | let _ = r.map_or(false, |x| x == 7); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(|x| x == 7)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:59:13 + --> tests/ui/unnecessary_map_or.rs:63:13 | LL | let _ = r.map_or(false, func); | ^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(func)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:60:13 + --> tests/ui/unnecessary_map_or.rs:64:13 | LL | let _ = Some(5).map_or(false, func); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(func)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:61:13 + --> tests/ui/unnecessary_map_or.rs:65:13 | LL | let _ = Some(5).map_or(true, func); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(func)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:66:13 + --> tests/ui/unnecessary_map_or.rs:70:13 | LL | let _ = r.map_or(false, |x| x == 8); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(r == Ok(8))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `r == Ok(8)` -error: aborting due to 18 previous errors +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/useless_vec.rs b/src/tools/clippy/tests/ui/useless_vec.rs new file mode 100644 index 000000000000..880809f81d7a --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_vec.rs @@ -0,0 +1,15 @@ +//@no-rustfix: no suggestions + +#![warn(clippy::useless_vec)] + +// Regression test for . +fn foo() { + // There should be no suggestion in this case. + let _some_variable = vec![ + //~^ useless_vec + 1, 2, // i'm here to stay + 3, 4, // but this one going away ;-; + ]; // that is life anyways +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/useless_vec.stderr b/src/tools/clippy/tests/ui/useless_vec.stderr new file mode 100644 index 000000000000..e47364fb06d3 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_vec.stderr @@ -0,0 +1,21 @@ +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:8:26 + | +LL | let _some_variable = vec![ + | __________________________^ +LL | | +LL | | 1, 2, // i'm here to stay +LL | | 3, 4, // but this one going away ;-; +LL | | ]; // that is life anyways + | |_____^ + | + = note: `-D clippy::useless-vec` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]` +help: you can use an array directly + | +LL ~ let _some_variable = [1, 2, // i'm here to stay +LL ~ 3, 4]; // that is life anyways + | + +error: aborting due to 1 previous error + diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 36f876bcdc60..4e2510ed9aba 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -39,18 +39,22 @@ macro_rules! string_enum { } impl FromStr for $name { - type Err = (); + type Err = String; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { match s { $($repr => Ok(Self::$variant),)* - _ => Err(()), + _ => Err(format!(concat!("unknown `", stringify!($name), "` variant: `{}`"), s)), } } } } } +// Make the macro visible outside of this module, for tests. +#[cfg(test)] +pub(crate) use string_enum; + string_enum! { #[derive(Clone, Copy, PartialEq, Debug)] pub enum Mode { 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/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 9e8443cd13c5..9eb6887f7e6f 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -32,7 +32,7 @@ use crate::{ColorConfig, json, stamp_file_path}; mod debugger; // Helper modules that implement test running logic for each test suite. -// tidy-alphabet-start +// tidy-alphabetical-start mod assembly; mod codegen; mod codegen_units; @@ -47,7 +47,7 @@ mod run_make; mod rustdoc; mod rustdoc_json; mod ui; -// tidy-alphabet-end +// tidy-alphabetical-end #[cfg(test)] mod tests; diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index ee7aed2a39c1..8a49d6305352 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -353,8 +353,8 @@ impl TestCx<'_> { // to work correctly. // // See for more background. + let stage0_sysroot = build_root.join("stage0-sysroot"); if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { - let stage0_sysroot = build_root.join("stage0-sysroot"); rustc.arg("--sysroot").arg(&stage0_sysroot); } @@ -373,6 +373,15 @@ impl TestCx<'_> { // Compute dynamic library search paths for recipes. let recipe_dylib_search_paths = { let mut paths = base_dylib_search_paths.clone(); + + // For stage 0, we need to explicitly include the stage0-sysroot libstd dylib. + // See . + if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { + paths.push( + stage0_sysroot.join("lib").join("rustlib").join(&self.config.host).join("lib"), + ); + } + paths.push(support_lib_path.parent().unwrap().to_path_buf()); paths.push(stage_std_path.join("rustlib").join(&self.config.host).join("lib")); paths diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs index fec746904ded..43c6dc0a67e8 100644 --- a/src/tools/compiletest/src/tests.rs +++ b/src/tools/compiletest/src/tests.rs @@ -66,3 +66,30 @@ fn is_test_test() { assert!(!is_test(&OsString::from("#a_dog_gif"))); assert!(!is_test(&OsString::from("~a_temp_file"))); } + +#[test] +fn string_enums() { + // These imports are needed for the macro-generated code + use std::fmt; + use std::str::FromStr; + + crate::common::string_enum! { + #[derive(Clone, Copy, Debug, PartialEq)] + enum Animal { + Cat => "meow", + Dog => "woof", + } + } + + // General assertions, mostly to silence the dead code warnings + assert_eq!(Animal::VARIANTS.len(), 2); + assert_eq!(Animal::STR_VARIANTS.len(), 2); + + // Correct string conversions + assert_eq!(Animal::Cat, "meow".parse().unwrap()); + assert_eq!(Animal::Dog, "woof".parse().unwrap()); + + // Invalid conversions + let animal = "nya".parse::(); + assert_eq!("unknown `Animal` variant: `nya`", animal.unwrap_err()); +} 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..fb2838a4ea03 100644 --- a/src/tools/replace-version-placeholder/src/main.rs +++ b/src/tools/replace-version-placeholder/src/main.rs @@ -10,13 +10,13 @@ fn main() { let version_str = t!(std::fs::read_to_string(&version_path), version_path); 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") - }, + &[ + &root_path.join("compiler"), + &root_path.join("library"), + &root_path.join("src/doc/rustc"), + &root_path.join("src/doc/rustdoc"), + ], + |path, _is_dir| walk::filter_dirs(path), &mut |entry, contents| { if !contents.contains(VERSION_PLACEHOLDER) { return; diff --git a/src/tools/run-make-support/src/external_deps/rustdoc.rs b/src/tools/run-make-support/src/external_deps/rustdoc.rs index df5e5d8a2e84..3c0e9c82f0bb 100644 --- a/src/tools/run-make-support/src/external_deps/rustdoc.rs +++ b/src/tools/run-make-support/src/external_deps/rustdoc.rs @@ -45,8 +45,7 @@ impl Rustdoc { #[track_caller] pub fn new() -> Self { let mut cmd = setup_common(); - let target_rpath_dir = env_var_os("TARGET_RPATH_DIR"); - cmd.arg(format!("-L{}", target_rpath_dir.to_string_lossy())); + cmd.arg("-L").arg(env_var_os("TARGET_RPATH_DIR")); Self { cmd } } 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.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 0c1f63880cd2..108171586ea8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -197,6 +197,7 @@ fn layout_of_simd_ty( align, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed: 0, })) } @@ -313,6 +314,7 @@ pub fn layout_of_ty_query( size, max_repr_align: None, unadjusted_abi_align: element.align.abi, + randomization_seed: 0, } } TyKind::Slice(element) => { @@ -326,6 +328,7 @@ pub fn layout_of_ty_query( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: element.align.abi, + randomization_seed: 0, } } TyKind::Str => Layout { @@ -337,6 +340,7 @@ pub fn layout_of_ty_query( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: dl.i8_align.abi, + randomization_seed: 0, }, // Potentially-wide pointers. TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => { 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 @@ ")) - { - Directive::Ignore(false) - } else { - Directive::Deny + return [Directive::Deny; N]; } + checks.map(|check| { + // Update `can_contain` when changing this + if contents.contains(&format!("// ignore-tidy-{check}")) + || contents.contains(&format!("# ignore-tidy-{check}")) + || contents.contains(&format!("/* ignore-tidy-{check} */")) + || contents.contains(&format!("")) + { + Directive::Ignore(false) + } else { + Directive::Deny + } + }) } macro_rules! suppressible_tidy_err { @@ -370,6 +394,7 @@ pub fn check(path: &Path, bad: &mut bool) { COLS }; + // When you change this, also change the `directive_line_starts` variable below let can_contain = contents.contains("// ignore-tidy-") || contents.contains("# ignore-tidy-") || contents.contains("/* ignore-tidy-") @@ -385,22 +410,19 @@ pub fn check(path: &Path, bad: &mut bool) { return; } } - let mut skip_cr = contains_ignore_directive(can_contain, &contents, "cr"); - let mut skip_undocumented_unsafe = - contains_ignore_directive(can_contain, &contents, "undocumented-unsafe"); - let mut skip_tab = contains_ignore_directive(can_contain, &contents, "tab"); - let mut skip_line_length = contains_ignore_directive(can_contain, &contents, "linelength"); - let mut skip_file_length = contains_ignore_directive(can_contain, &contents, "filelength"); - let mut skip_end_whitespace = - contains_ignore_directive(can_contain, &contents, "end-whitespace"); - let mut skip_trailing_newlines = - contains_ignore_directive(can_contain, &contents, "trailing-newlines"); - let mut skip_leading_newlines = - contains_ignore_directive(can_contain, &contents, "leading-newlines"); - let mut skip_copyright = contains_ignore_directive(can_contain, &contents, "copyright"); - let mut skip_dbg = contains_ignore_directive(can_contain, &contents, "dbg"); - let mut skip_odd_backticks = - contains_ignore_directive(can_contain, &contents, "odd-backticks"); + let [ + mut skip_cr, + mut skip_undocumented_unsafe, + mut skip_tab, + mut skip_line_length, + mut skip_file_length, + mut skip_end_whitespace, + mut skip_trailing_newlines, + mut skip_leading_newlines, + mut skip_copyright, + mut skip_dbg, + mut skip_odd_backticks, + ] = contains_ignore_directives(can_contain, &contents, CONFIGURABLE_CHECKS); let mut leading_new_lines = false; let mut trailing_new_lines = 0; let mut lines = 0; @@ -474,6 +496,22 @@ pub fn check(path: &Path, bad: &mut bool) { suppressible_tidy_err!(err, skip_cr, "CR character"); } if !is_this_file { + let directive_line_starts = ["// ", "# ", "/* ", " $DIR/debug.rs:15:1 + --> $DIR/debug.rs:16:1 | LL | fn test(_x: u8) -> bool { true } | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -128,6 +130,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -165,6 +168,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -181,7 +185,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:18:1 + --> $DIR/debug.rs:19:1 | LL | type TestFnPtr = fn(bool) -> u8; | ^^^^^^^^^^^^^^ @@ -214,6 +218,7 @@ error: fn_abi_of(test_generic) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -248,6 +253,7 @@ error: fn_abi_of(test_generic) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -257,13 +263,13 @@ error: fn_abi_of(test_generic) = FnAbi { conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:21:1 + --> $DIR/debug.rs:22:1 | LL | fn test_generic(_x: *const T) { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions - --> $DIR/debug.rs:24:1 + --> $DIR/debug.rs:25:1 | LL | const C: () = (); | ^^^^^^^^^^^ @@ -296,6 +302,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -330,6 +337,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -366,6 +374,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -400,6 +409,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -409,7 +419,7 @@ error: ABIs are not compatible conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:40:1 + --> $DIR/debug.rs:41:1 | LL | type TestAbiNe = (fn(u8), fn(u32)); | ^^^^^^^^^^^^^^ @@ -439,6 +449,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Indirect { @@ -477,6 +488,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -510,6 +522,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Indirect { @@ -548,6 +561,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -557,7 +571,7 @@ error: ABIs are not compatible conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:43:1 + --> $DIR/debug.rs:44:1 | LL | type TestAbiNeLarger = (fn([u8; 32]), fn([u32; 32])); | ^^^^^^^^^^^^^^^^^^^^ @@ -589,6 +603,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -623,6 +638,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -659,6 +675,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -693,6 +710,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -702,7 +720,7 @@ error: ABIs are not compatible conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:46:1 + --> $DIR/debug.rs:47:1 | LL | type TestAbiNeFloat = (fn(f32), fn(u32)); | ^^^^^^^^^^^^^^^^^^^ @@ -735,6 +753,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -769,6 +788,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -805,6 +825,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -839,6 +860,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -848,13 +870,13 @@ error: ABIs are not compatible conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:50:1 + --> $DIR/debug.rs:51:1 | LL | type TestAbiNeSign = (fn(i32), fn(u32)); | ^^^^^^^^^^^^^^^^^^ error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/debug.rs:53:46 + --> $DIR/debug.rs:54:46 | LL | type TestAbiEqNonsense = (fn((str, str)), fn((str, str))); | ^^^^^^^^^^ doesn't have a size known at compile-time @@ -863,7 +885,7 @@ LL | type TestAbiEqNonsense = (fn((str, str)), fn((str, str))); = note: only the last element of a tuple may have a dynamically sized type error: `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions - --> $DIR/debug.rs:28:5 + --> $DIR/debug.rs:29:5 | LL | const C: () = (); | ^^^^^^^^^^^ @@ -906,6 +928,7 @@ error: fn_abi_of(assoc_test) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -942,6 +965,7 @@ error: fn_abi_of(assoc_test) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -951,7 +975,7 @@ error: fn_abi_of(assoc_test) = FnAbi { conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:33:5 + --> $DIR/debug.rs:34:5 | LL | fn assoc_test(&self) { } | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/sysv64-zst.stderr b/tests/ui/abi/sysv64-zst.stderr index 8e1791e27d27..781e9b2f4c9d 100644 --- a/tests/ui/abi/sysv64-zst.stderr +++ b/tests/ui/abi/sysv64-zst.stderr @@ -22,6 +22,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, @@ -49,6 +50,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, diff --git a/tests/ui/abi/win64-zst.x86_64-linux.stderr b/tests/ui/abi/win64-zst.x86_64-linux.stderr index 76d90670eb1d..a28a59fdd8d0 100644 --- a/tests/ui/abi/win64-zst.x86_64-linux.stderr +++ b/tests/ui/abi/win64-zst.x86_64-linux.stderr @@ -22,6 +22,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, @@ -49,6 +50,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, diff --git a/tests/ui/abi/win64-zst.x86_64-windows-gnu.stderr b/tests/ui/abi/win64-zst.x86_64-windows-gnu.stderr index 7ee90e247441..cf0cc00c5ed0 100644 --- a/tests/ui/abi/win64-zst.x86_64-windows-gnu.stderr +++ b/tests/ui/abi/win64-zst.x86_64-windows-gnu.stderr @@ -22,6 +22,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Indirect { @@ -60,6 +61,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, diff --git a/tests/ui/abi/win64-zst.x86_64-windows-msvc.stderr b/tests/ui/abi/win64-zst.x86_64-windows-msvc.stderr index 76d90670eb1d..a28a59fdd8d0 100644 --- a/tests/ui/abi/win64-zst.x86_64-windows-msvc.stderr +++ b/tests/ui/abi/win64-zst.x86_64-windows-msvc.stderr @@ -22,6 +22,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, @@ -49,6 +50,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, diff --git a/tests/ui/associated-types/associated-types-for-unimpl-trait.stderr b/tests/ui/associated-types/associated-types-for-unimpl-trait.stderr index 4cba3990049a..db18cdf7d5aa 100644 --- a/tests/ui/associated-types/associated-types-for-unimpl-trait.stderr +++ b/tests/ui/associated-types/associated-types-for-unimpl-trait.stderr @@ -10,11 +10,12 @@ LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: S | +++++++++++ error[E0277]: the trait bound `Self: Get` is not satisfied - --> $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/exhaustive-names-values.empty_cfg.stderr b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr index d49ed3e75518..23b6edacce77 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr @@ -15,7 +15,7 @@ warning: unexpected `cfg` condition value: `value` LL | #[cfg(target_vendor = "value")] | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `feature` diff --git a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr index 81dbbca4f863..804d7fb9163d 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr @@ -15,7 +15,7 @@ warning: unexpected `cfg` condition value: `value` LL | #[cfg(target_vendor = "value")] | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `unk` diff --git a/tests/ui/check-cfg/exhaustive-names-values.full.stderr b/tests/ui/check-cfg/exhaustive-names-values.full.stderr index 81dbbca4f863..804d7fb9163d 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.full.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.full.stderr @@ -15,7 +15,7 @@ warning: unexpected `cfg` condition value: `value` LL | #[cfg(target_vendor = "value")] | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `unk` 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/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index ab69938f51ae..5c1898a0ae36 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -230,7 +230,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_vendor = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` diff --git a/tests/ui/coherence/auxiliary/pr_review_132289_2_lib.rs b/tests/ui/coherence/auxiliary/pr_review_132289_2_lib.rs new file mode 100644 index 000000000000..4b79147dce10 --- /dev/null +++ b/tests/ui/coherence/auxiliary/pr_review_132289_2_lib.rs @@ -0,0 +1,37 @@ +pub type A = &'static [usize; 1]; +pub type B = &'static [usize; 100]; + +pub trait Trait

{ + type Assoc; +} + +pub type Dyn

= dyn Trait; + +pub trait LocallyUnimplemented

{} + +impl Trait

for T +where + T: LocallyUnimplemented

, +{ + type Assoc = B; +} + +trait MakeArray { + fn make() -> &'static Arr; +} +impl MakeArray<[usize; N]> for () { + fn make() -> &'static [usize; N] { + &[1337; N] + } +} + +// it would be sound for this return type to be interpreted as being +// either of A or B, if that's what a soundness fix for overlap of +// dyn Trait's impls would entail + +// In this test, we check at the call-site that the interpretation +// is consistent across crates in this specific scenario. +pub fn function

() -> ( as Trait

>::Assoc, usize) { + let val = <() as MakeArray<_>>::make(); + (val, val.len()) +} diff --git a/tests/ui/coherence/auxiliary/pr_review_132289_3_lib.rs b/tests/ui/coherence/auxiliary/pr_review_132289_3_lib.rs new file mode 100644 index 000000000000..f90be3b2487e --- /dev/null +++ b/tests/ui/coherence/auxiliary/pr_review_132289_3_lib.rs @@ -0,0 +1,12 @@ +use std::ops::Index; + +pub trait Trait { + fn f(&self) + where + dyn Index<(), Output = ()>: Index<()>; + // rustc (correctly) determines ^^^^^^^^ this bound to be true +} + +pub fn call(x: &dyn Trait) { + x.f(); // so we can call `f` +} diff --git a/tests/ui/coherence/pr-review-132289-1.rs b/tests/ui/coherence/pr-review-132289-1.rs new file mode 100644 index 000000000000..ab3ed9655e07 --- /dev/null +++ b/tests/ui/coherence/pr-review-132289-1.rs @@ -0,0 +1,52 @@ +// This is a regression test for issues that came up during review of the (closed) +// PR #132289; this single-crate test case is +// the first example from @steffahn during review. +// https://github.com/rust-lang/rust/pull/132289#issuecomment-2564492153 + +//@ check-pass + +type A = &'static [usize; 1]; +type B = &'static [usize; 100]; + +type DynSomething = dyn Something; + +trait Super { + type Assoc; +} +impl Super for Foo { + type Assoc = A; +} + +trait IsDynSomething {} +impl IsDynSomething for DynSomething {} + +impl Super for T +where + T: IsDynSomething, +{ + type Assoc = B; +} + +trait Something: Super { + fn method(&self) -> Self::Assoc; +} + +struct Foo; +impl Something for Foo { + fn method(&self) -> Self::Assoc { + &[1337] + } +} + +fn main() { + let x = &Foo; + let y: &DynSomething = x; + + // no surprises here + let _arr1: A = x.method(); + + // this (`_arr2`) can't ever become B either, soundly + let _arr2: A = y.method(); + // there aren't any other arrays being defined anywhere in this + // test case, besides the length-1 one containing [1337] +} diff --git a/tests/ui/coherence/pr-review-132289-2.rs b/tests/ui/coherence/pr-review-132289-2.rs new file mode 100644 index 000000000000..95ad86c61ff1 --- /dev/null +++ b/tests/ui/coherence/pr-review-132289-2.rs @@ -0,0 +1,26 @@ +// This is a regression test for issues that came up during review of the (closed) +// PR #132289; this 2-crate test case is adapted from +// the second example from @steffahn during review. +// https://github.com/rust-lang/rust/pull/132289#issuecomment-2564587796 + +//@ run-pass +//@ aux-build: pr_review_132289_2_lib.rs + +extern crate pr_review_132289_2_lib; + +use pr_review_132289_2_lib::{function, Dyn, LocallyUnimplemented}; + +struct Param; + +impl LocallyUnimplemented for Dyn {} + +// it would be sound for `function::`'s return type to be +// either of A or B, if that's what a soundness fix for overlap of +// dyn Trait's impls would entail + +// In this test, we check at this call-site that the interpretation +// is consistent with the function definition's body. +fn main() { + let (arr, len) = function::(); + assert_eq!(arr.len(), len); +} diff --git a/tests/ui/coherence/pr-review-132289-3.rs b/tests/ui/coherence/pr-review-132289-3.rs new file mode 100644 index 000000000000..7e597baa6ec2 --- /dev/null +++ b/tests/ui/coherence/pr-review-132289-3.rs @@ -0,0 +1,50 @@ +// This is a regression test for issues that came up during review of the (closed) +// PR #132289; this 3-ish-crate (including std) test case is adapted from +// the third example from @steffahn during review. +// https://github.com/rust-lang/rust/pull/132289#issuecomment-2564599221 + +//@ run-pass +//@ check-run-results +//@ aux-build: pr_review_132289_3_lib.rs + +extern crate pr_review_132289_3_lib; + +use std::ops::Index; + +use pr_review_132289_3_lib::{call, Trait}; + +trait SubIndex: Index {} + +struct Param; + +trait Project { + type Ty: ?Sized; +} +impl Project for () { + type Ty = dyn SubIndex; +} + +impl Index for <() as Project>::Ty { + type Output = (); + + fn index(&self, _: Param) -> &() { + &() + } +} + +struct Struct; + +impl Trait for Struct { + fn f(&self) + where + // higher-ranked to allow potentially-false bounds + for<'a> dyn Index<(), Output = ()>: Index<()>, + // after #132289 rustc used to believe this bound false + { + println!("hello!"); + } +} + +fn main() { + call(&Struct); // <- would segfault if the method `f` wasn't part of the vtable +} diff --git a/tests/ui/coherence/pr-review-132289-3.run.stdout b/tests/ui/coherence/pr-review-132289-3.run.stdout new file mode 100644 index 000000000000..4effa19f4f75 --- /dev/null +++ b/tests/ui/coherence/pr-review-132289-3.run.stdout @@ -0,0 +1 @@ +hello! diff --git a/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs b/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs index c1d3321f8407..d6bfe6fde82c 100644 --- a/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs +++ b/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs @@ -15,6 +15,7 @@ fn f( }], ) -> impl Iterator { //~^ ERROR expected a type, found a trait +//~| ERROR expected a type, found a trait //~| ERROR `()` is not an iterator } diff --git a/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.stderr b/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.stderr index 5c4d643a28e9..80d711ca844a 100644 --- a/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.stderr +++ b/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.stderr @@ -16,14 +16,6 @@ help: you might be missing a type parameter LL | fn f( | +++ -error[E0277]: `()` is not an iterator - --> $DIR/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs:16:6 - | -LL | ) -> impl Iterator { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not an iterator - | - = help: the trait `Iterator` is not implemented for `()` - error[E0782]: expected a type, found a trait --> $DIR/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs:16:27 | @@ -39,7 +31,31 @@ help: you might have meant to write a bound here LL | ) -> impl Iterator { | ~ -error: aborting due to 3 previous errors +error[E0782]: expected a type, found a trait + --> $DIR/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs:16:27 + | +LL | ) -> impl Iterator { + | ^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: you can add the `dyn` keyword if you want a trait object + | +LL | ) -> impl Iterator { + | +++ +help: you might have meant to write a bound here + | +LL | ) -> impl Iterator { + | ~ + +error[E0277]: `()` is not an iterator + --> $DIR/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs:16:6 + | +LL | ) -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not an iterator + | + = help: the trait `Iterator` is not implemented for `()` + +error: aborting due to 4 previous errors Some errors have detailed explanations: E0277, E0412, E0782. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/generic_const_exprs/lit_type_mismatch.rs b/tests/ui/const-generics/generic_const_exprs/lit_type_mismatch.rs new file mode 100644 index 000000000000..1ed0965e1bde --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/lit_type_mismatch.rs @@ -0,0 +1,22 @@ +//! ICE regression test for #114317 and #126182 +//! Type mismatches of literals cause errors int typeck, +//! but those errors cannot be propagated to the various +//! `lit_to_const` call sites. Now `lit_to_const` just delays +//! a bug and produces an error constant on its own. + +#![feature(adt_const_params)] +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +struct A(C); +//~^ ERROR: generic parameters with a default must be trailing +//~| ERROR: mismatched types + +struct Cond; + +struct Thing>(T); +//~^ ERROR: mismatched types + +impl Thing {} + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/lit_type_mismatch.stderr b/tests/ui/const-generics/generic_const_exprs/lit_type_mismatch.stderr new file mode 100644 index 000000000000..e4613e498b27 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/lit_type_mismatch.stderr @@ -0,0 +1,21 @@ +error: generic parameters with a default must be trailing + --> $DIR/lit_type_mismatch.rs:11:16 + | +LL | struct A(C); + | ^ + +error[E0308]: mismatched types + --> $DIR/lit_type_mismatch.rs:11:24 + | +LL | struct A(C); + | ^ expected `()`, found integer + +error[E0308]: mismatched types + --> $DIR/lit_type_mismatch.rs:17:23 + | +LL | struct Thing>(T); + | ^ expected `bool`, found integer + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. 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/const-generics/min_const_generics/invalid-patterns.32bit.stderr b/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr index be92429e3abc..11a824ba73b2 100644 --- a/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr +++ b/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr @@ -1,27 +1,3 @@ -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:29:21 - | -LL | get_flag::(); - | ^^^^ expected `char`, found `u8` - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:31:14 - | -LL | get_flag::<7, 'c'>(); - | ^ expected `bool`, found integer - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:14 - | -LL | get_flag::<42, 0x5ad>(); - | ^^ expected `bool`, found integer - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:18 - | -LL | get_flag::<42, 0x5ad>(); - | ^^^^^ expected `char`, found `u8` - error[E0080]: evaluation of constant value failed --> $DIR/invalid-patterns.rs:38:32 | @@ -56,6 +32,30 @@ error[E0080]: evaluation of constant value failed LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:29:21 + | +LL | get_flag::(); + | ^^^^ expected `char`, found `u8` + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:31:14 + | +LL | get_flag::<7, 'c'>(); + | ^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:33:14 + | +LL | get_flag::<42, 0x5ad>(); + | ^^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:33:18 + | +LL | get_flag::<42, 0x5ad>(); + | ^^^^^ expected `char`, found `u8` + error: aborting due to 8 previous errors Some errors have detailed explanations: E0080, E0308. diff --git a/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr b/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr index be92429e3abc..11a824ba73b2 100644 --- a/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr +++ b/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr @@ -1,27 +1,3 @@ -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:29:21 - | -LL | get_flag::(); - | ^^^^ expected `char`, found `u8` - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:31:14 - | -LL | get_flag::<7, 'c'>(); - | ^ expected `bool`, found integer - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:14 - | -LL | get_flag::<42, 0x5ad>(); - | ^^ expected `bool`, found integer - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:18 - | -LL | get_flag::<42, 0x5ad>(); - | ^^^^^ expected `char`, found `u8` - error[E0080]: evaluation of constant value failed --> $DIR/invalid-patterns.rs:38:32 | @@ -56,6 +32,30 @@ error[E0080]: evaluation of constant value failed LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:29:21 + | +LL | get_flag::(); + | ^^^^ expected `char`, found `u8` + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:31:14 + | +LL | get_flag::<7, 'c'>(); + | ^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:33:14 + | +LL | get_flag::<42, 0x5ad>(); + | ^^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:33:18 + | +LL | get_flag::<42, 0x5ad>(); + | ^^^^^ expected `char`, found `u8` + error: aborting due to 8 previous errors Some errors have detailed explanations: E0080, E0308. 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/consts/const_in_pattern/non_structural_with_escaping_bounds.rs b/tests/ui/consts/const_in_pattern/non_structural_with_escaping_bounds.rs new file mode 100644 index 000000000000..e5d095fd6178 --- /dev/null +++ b/tests/ui/consts/const_in_pattern/non_structural_with_escaping_bounds.rs @@ -0,0 +1,15 @@ +#![feature(structural_match)] +impl std::marker::StructuralPartialEq for O { } + +enum O { + Some(*const T), + None, +} + +const C: O Fn(Box)> = O::None; + +fn main() { + match O::None { + C => (), //~ ERROR constant of non-structural type + } +} diff --git a/tests/ui/consts/const_in_pattern/non_structural_with_escaping_bounds.stderr b/tests/ui/consts/const_in_pattern/non_structural_with_escaping_bounds.stderr new file mode 100644 index 000000000000..371be9982f70 --- /dev/null +++ b/tests/ui/consts/const_in_pattern/non_structural_with_escaping_bounds.stderr @@ -0,0 +1,13 @@ +error: constant of non-structural type `O Fn(Box)>` in a pattern + --> $DIR/non_structural_with_escaping_bounds.rs:13:9 + | +LL | const C: O Fn(Box)> = O::None; + | ----------------------------------------------- constant defined here +... +LL | C => (), + | ^ constant of non-structural type + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details + +error: aborting due to 1 previous error + diff --git a/tests/ui/consts/fn_trait_refs.stderr b/tests/ui/consts/fn_trait_refs.stderr index e0dbecff8e58..d688bfbde2bc 100644 --- a/tests/ui/consts/fn_trait_refs.stderr +++ b/tests/ui/consts/fn_trait_refs.stderr @@ -155,90 +155,21 @@ note: `FnMut` can't be used with `~const` because it isn't annotated with `#[con --> $SRC_DIR/core/src/ops/function.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: the trait bound `fn() -> i32 {one}: const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:70:32 +error[E0015]: cannot call non-const operator in constants + --> $DIR/fn_trait_refs.rs:71:17 | -LL | let test_one = test_fn(one); - | ------- ^^^ - | | - | required by a bound introduced by this call +LL | assert!(test_one == (1, 1, 1)); + | ^^^^^^^^^^^^^^^^^^^^^ | -note: required by a bound in `test_fn` - --> $DIR/fn_trait_refs.rs:35:24 - | -LL | const fn test_fn(mut f: T) -> (T::Output, T::Output, T::Output) - | ------- required by a bound in this function -LL | where -LL | T: ~const Fn<()> + ~const Destruct, - | ^^^^^^ required by this bound in `test_fn` + = note: calls in constants are limited to constant functions, tuple structs and tuple variants -error[E0277]: the trait bound `fn() -> i32 {two}: const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:73:36 +error[E0015]: cannot call non-const operator in constants + --> $DIR/fn_trait_refs.rs:74:17 | -LL | let test_two = test_fn_mut(two); - | ----------- ^^^ - | | - | required by a bound introduced by this call +LL | assert!(test_two == (2, 2)); + | ^^^^^^^^^^^^^^^^^^ | -note: required by a bound in `test_fn_mut` - --> $DIR/fn_trait_refs.rs:49:27 - | -LL | const fn test_fn_mut(mut f: T) -> (T::Output, T::Output) - | ----------- required by a bound in this function -LL | where -LL | T: ~const FnMut<()> + ~const Destruct, - | ^^^^^^ required by this bound in `test_fn_mut` - -error[E0277]: the trait bound `&T: ~const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:39:19 - | -LL | tester_fn(&f), - | --------- ^^ - | | - | required by a bound introduced by this call - | -note: required by a bound in `tester_fn` - --> $DIR/fn_trait_refs.rs:14:24 - | -LL | const fn tester_fn(f: T) -> T::Output - | --------- required by a bound in this function -LL | where -LL | T: ~const Fn<()> + ~const Destruct, - | ^^^^^^ required by this bound in `tester_fn` - -error[E0277]: the trait bound `&T: ~const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:41:23 - | -LL | tester_fn_mut(&f), - | ------------- ^^ - | | - | required by a bound introduced by this call - | -note: required by a bound in `tester_fn_mut` - --> $DIR/fn_trait_refs.rs:21:27 - | -LL | const fn tester_fn_mut(mut f: T) -> T::Output - | ------------- required by a bound in this function -LL | where -LL | T: ~const FnMut<()> + ~const Destruct, - | ^^^^^^ required by this bound in `tester_fn_mut` - -error[E0277]: the trait bound `&mut T: ~const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:53:23 - | -LL | tester_fn_mut(&mut f), - | ------------- ^^^^^^ - | | - | required by a bound introduced by this call - | -note: required by a bound in `tester_fn_mut` - --> $DIR/fn_trait_refs.rs:21:27 - | -LL | const fn tester_fn_mut(mut f: T) -> T::Output - | ------------- required by a bound in this function -LL | where -LL | T: ~const FnMut<()> + ~const Destruct, - | ^^^^^^ required by this bound in `tester_fn_mut` + = note: calls in constants are limited to constant functions, tuple structs and tuple variants error[E0015]: cannot call non-const closure in constant functions --> $DIR/fn_trait_refs.rs:16:5 @@ -264,7 +195,7 @@ LL | f() | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants -error: aborting due to 25 previous errors +error: aborting due to 22 previous errors -Some errors have detailed explanations: E0015, E0277, E0635. +Some errors have detailed explanations: E0015, E0635. For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/consts/promoted_const_call.stderr b/tests/ui/consts/promoted_const_call.stderr index dd70bb601c47..40c6d083b066 100644 --- a/tests/ui/consts/promoted_const_call.stderr +++ b/tests/ui/consts/promoted_const_call.stderr @@ -5,6 +5,10 @@ LL | let _: &'static _ = &id(&Panic); | ^^^^^ - value is dropped here | | | the destructor for this type cannot be evaluated in constants + | + = note: see issue #133214 for more information + = help: add `#![feature(const_destruct)]` 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[E0716]: temporary value dropped while borrowed --> $DIR/promoted_const_call.rs:16:26 diff --git a/tests/ui/coroutine/gen_block.e2024.stderr b/tests/ui/coroutine/gen_block.e2024.stderr index 322259cf2f84..0491bdbc2e17 100644 --- a/tests/ui/coroutine/gen_block.e2024.stderr +++ b/tests/ui/coroutine/gen_block.e2024.stderr @@ -1,4 +1,4 @@ -error[E0658]: the `#[coroutines]` attribute is an experimental feature +error[E0658]: the `#[coroutine]` attribute is an experimental feature --> $DIR/gen_block.rs:20:13 | LL | let _ = #[coroutine] || yield true; @@ -8,7 +8,7 @@ LL | let _ = #[coroutine] || yield true; = help: add `#![feature(coroutines)]` 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]: the `#[coroutines]` attribute is an experimental feature +error[E0658]: the `#[coroutine]` attribute is an experimental feature --> $DIR/gen_block.rs:24:13 | LL | let _ = #[coroutine] || {}; diff --git a/tests/ui/coroutine/gen_block.none.stderr b/tests/ui/coroutine/gen_block.none.stderr index 15123a49e485..43437793005a 100644 --- a/tests/ui/coroutine/gen_block.none.stderr +++ b/tests/ui/coroutine/gen_block.none.stderr @@ -44,7 +44,7 @@ LL | let _ = #[coroutine] || yield true; = help: add `#![feature(coroutines)]` 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]: the `#[coroutines]` attribute is an experimental feature +error[E0658]: the `#[coroutine]` attribute is an experimental feature --> $DIR/gen_block.rs:20:13 | LL | let _ = #[coroutine] || yield true; @@ -54,7 +54,7 @@ LL | let _ = #[coroutine] || yield true; = help: add `#![feature(coroutines)]` 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]: the `#[coroutines]` attribute is an experimental feature +error[E0658]: the `#[coroutine]` attribute is an experimental feature --> $DIR/gen_block.rs:24:13 | LL | let _ = #[coroutine] || {}; diff --git a/tests/ui/coroutine/gen_block.rs b/tests/ui/coroutine/gen_block.rs index 6734de3b667d..4494d654eeb6 100644 --- a/tests/ui/coroutine/gen_block.rs +++ b/tests/ui/coroutine/gen_block.rs @@ -18,9 +18,9 @@ fn main() { //~^^ ERROR `yield` can only be used in let _ = #[coroutine] || yield true; //[none]~ ERROR yield syntax is experimental - //~^ ERROR `#[coroutines]` attribute is an experimental feature + //~^ ERROR `#[coroutine]` attribute is an experimental feature //~^^ ERROR yield syntax is experimental let _ = #[coroutine] || {}; - //~^ ERROR `#[coroutines]` attribute is an experimental feature + //~^ ERROR `#[coroutine]` attribute is an experimental feature } 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/dyn-keyword/misspelled-associated-item.rs b/tests/ui/dyn-keyword/misspelled-associated-item.rs new file mode 100644 index 000000000000..8319e797d621 --- /dev/null +++ b/tests/ui/dyn-keyword/misspelled-associated-item.rs @@ -0,0 +1,12 @@ +//@ edition: 2021 + +trait Trait { + fn typo() -> Self; +} + +fn main() { + let () = Trait::typoe(); + //~^ ERROR expected a type, found a trait + //~| HELP you can add the `dyn` keyword if you want a trait object + //~| HELP you may have misspelled this associated item +} diff --git a/tests/ui/dyn-keyword/misspelled-associated-item.stderr b/tests/ui/dyn-keyword/misspelled-associated-item.stderr new file mode 100644 index 000000000000..4211889e524c --- /dev/null +++ b/tests/ui/dyn-keyword/misspelled-associated-item.stderr @@ -0,0 +1,18 @@ +error[E0782]: expected a type, found a trait + --> $DIR/misspelled-associated-item.rs:8:14 + | +LL | let () = Trait::typoe(); + | ^^^^^ + | +help: you can add the `dyn` keyword if you want a trait object + | +LL | let () = ::typoe(); + | ++++ + +help: you may have misspelled this associated item, causing `Trait` to be interpreted as a type rather than a trait + | +LL | let () = Trait::typo(); + | ~~~~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0782`. 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/force-inlining/asm.rs b/tests/ui/force-inlining/asm.rs new file mode 100644 index 000000000000..85014ffa5158 --- /dev/null +++ b/tests/ui/force-inlining/asm.rs @@ -0,0 +1,68 @@ +//@ build-fail +//@ compile-flags: --crate-type=lib --target thumbv4t-none-eabi +//@ needs-llvm-components: arm + +// Checks that forced inlining won't mix asm with incompatible instruction sets. + +#![crate_type = "lib"] +#![feature(rustc_attrs)] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} +#[lang = "freeze"] +pub unsafe trait Freeze {} + +#[lang = "start"] +fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { + 0 +} + +#[rustc_builtin_macro] +#[macro_export] +macro_rules! asm { + ("assembly template", + $(operands,)* + $(options($(option),*))? + ) => { + /* compiler built-in */ + }; +} + +#[instruction_set(arm::a32)] +#[rustc_force_inline] +fn instruction_set_a32() {} + +#[instruction_set(arm::t32)] +#[rustc_force_inline] +fn instruction_set_t32() {} + +#[rustc_force_inline] +fn instruction_set_default() {} + +#[rustc_force_inline] +fn inline_always_and_using_inline_asm() { + unsafe { asm!("/* do nothing */") }; +} + +#[instruction_set(arm::t32)] +pub fn t32() { + instruction_set_a32(); +//~^ ERROR `instruction_set_a32` could not be inlined into `t32` but is required to be inlined + instruction_set_t32(); + instruction_set_default(); + inline_always_and_using_inline_asm(); +//~^ ERROR `inline_always_and_using_inline_asm` could not be inlined into `t32` but is required to be inlined +} + +pub fn default() { + instruction_set_a32(); +//~^ ERROR `instruction_set_a32` could not be inlined into `default` but is required to be inlined + instruction_set_t32(); +//~^ ERROR `instruction_set_t32` could not be inlined into `default` but is required to be inlined + instruction_set_default(); + inline_always_and_using_inline_asm(); +} diff --git a/tests/ui/force-inlining/asm.stderr b/tests/ui/force-inlining/asm.stderr new file mode 100644 index 000000000000..ef04688965bb --- /dev/null +++ b/tests/ui/force-inlining/asm.stderr @@ -0,0 +1,34 @@ +error: `instruction_set_a32` could not be inlined into `t32` but is required to be inlined + --> $DIR/asm.rs:53:5 + | +LL | instruction_set_a32(); + | ^^^^^^^^^^^^^^^^^^^^^ ...`instruction_set_a32` called here + | + = note: could not be inlined due to: incompatible instruction set + +error: `inline_always_and_using_inline_asm` could not be inlined into `t32` but is required to be inlined + --> $DIR/asm.rs:57:5 + | +LL | inline_always_and_using_inline_asm(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...`inline_always_and_using_inline_asm` called here + | + = note: could not be inlined due to: cannot move inline-asm across instruction sets + +error: `instruction_set_a32` could not be inlined into `default` but is required to be inlined + --> $DIR/asm.rs:62:5 + | +LL | instruction_set_a32(); + | ^^^^^^^^^^^^^^^^^^^^^ ...`instruction_set_a32` called here + | + = note: could not be inlined due to: incompatible instruction set + +error: `instruction_set_t32` could not be inlined into `default` but is required to be inlined + --> $DIR/asm.rs:64:5 + | +LL | instruction_set_t32(); + | ^^^^^^^^^^^^^^^^^^^^^ ...`instruction_set_t32` called here + | + = note: could not be inlined due to: incompatible instruction set + +error: aborting due to 4 previous errors + diff --git a/tests/ui/force-inlining/auxiliary/callees.rs b/tests/ui/force-inlining/auxiliary/callees.rs new file mode 100644 index 000000000000..7468b8f10985 --- /dev/null +++ b/tests/ui/force-inlining/auxiliary/callees.rs @@ -0,0 +1,10 @@ +//@ compile-flags: --crate-type=lib +#![feature(rustc_attrs)] + +#[rustc_force_inline = "the test requires it"] +pub fn forced_with_reason() { +} + +#[rustc_force_inline] +pub fn forced() { +} diff --git a/tests/ui/force-inlining/cast.rs b/tests/ui/force-inlining/cast.rs new file mode 100644 index 000000000000..8f51ec839773 --- /dev/null +++ b/tests/ui/force-inlining/cast.rs @@ -0,0 +1,25 @@ +//@ check-fail +//@ compile-flags: --crate-type=lib +#![allow(internal_features)] +#![feature(rustc_attrs)] + +#[rustc_force_inline] +pub fn callee(x: isize) -> usize { unimplemented!() } + +fn a() { + let _: fn(isize) -> usize = callee; +//~^ ERROR cannot coerce functions which must be inlined to function pointers +} + +fn b() { + let _ = callee as fn(isize) -> usize; +//~^ ERROR non-primitive cast +} + +fn c() { + let _: [fn(isize) -> usize; 2] = [ + callee, +//~^ ERROR cannot coerce functions which must be inlined to function pointers + callee, + ]; +} diff --git a/tests/ui/force-inlining/cast.stderr b/tests/ui/force-inlining/cast.stderr new file mode 100644 index 000000000000..116919e5fe7f --- /dev/null +++ b/tests/ui/force-inlining/cast.stderr @@ -0,0 +1,40 @@ +error[E0308]: cannot coerce functions which must be inlined to function pointers + --> $DIR/cast.rs:10:33 + | +LL | let _: fn(isize) -> usize = callee; + | ------------------ ^^^^^^ cannot coerce functions which must be inlined to function pointers + | | + | expected due to this + | + = note: expected fn pointer `fn(_) -> _` + found fn item `fn(_) -> _ {callee}` + = note: fn items are distinct from fn pointers +help: consider casting to a fn pointer + | +LL | let _: fn(isize) -> usize = callee as fn(isize) -> usize; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0605]: non-primitive cast: `fn(isize) -> usize {callee}` as `fn(isize) -> usize` + --> $DIR/cast.rs:15:13 + | +LL | let _ = callee as fn(isize) -> usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast + +error[E0308]: cannot coerce functions which must be inlined to function pointers + --> $DIR/cast.rs:21:9 + | +LL | callee, + | ^^^^^^ cannot coerce functions which must be inlined to function pointers + | + = note: expected fn pointer `fn(_) -> _` + found fn item `fn(_) -> _ {callee}` + = note: fn items are distinct from fn pointers +help: consider casting to a fn pointer + | +LL | callee as fn(isize) -> usize, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0605. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/force-inlining/cross-crate.rs b/tests/ui/force-inlining/cross-crate.rs new file mode 100644 index 000000000000..a3f0c2c873ea --- /dev/null +++ b/tests/ui/force-inlining/cross-crate.rs @@ -0,0 +1,13 @@ +//@ aux-build:callees.rs +//@ build-pass +//@ compile-flags: --crate-type=lib + +extern crate callees; + +// Test that forced inlining across crates works as expected. + +pub fn caller() { + callees::forced(); + + callees::forced_with_reason(); +} diff --git a/tests/ui/force-inlining/deny-async.rs b/tests/ui/force-inlining/deny-async.rs new file mode 100644 index 000000000000..bdf6271bd2a1 --- /dev/null +++ b/tests/ui/force-inlining/deny-async.rs @@ -0,0 +1,24 @@ +//@ check-fail +//@ compile-flags: --crate-type=lib +//@ edition: 2021 +#![allow(internal_features)] +#![feature(rustc_attrs)] + +// Test that forced inlining into async functions w/ errors works as expected. + +#[rustc_no_mir_inline] +#[rustc_force_inline] +//~^ ERROR `callee` is incompatible with `#[rustc_force_inline]` +pub fn callee() { +} + +#[rustc_no_mir_inline] +#[rustc_force_inline = "the test requires it"] +//~^ ERROR `callee_justified` is incompatible with `#[rustc_force_inline]` +pub fn callee_justified() { +} + +async fn async_caller() { + callee(); + callee_justified(); +} diff --git a/tests/ui/force-inlining/deny-async.stderr b/tests/ui/force-inlining/deny-async.stderr new file mode 100644 index 000000000000..302ca4190710 --- /dev/null +++ b/tests/ui/force-inlining/deny-async.stderr @@ -0,0 +1,24 @@ +error: `callee` is incompatible with `#[rustc_force_inline]` + --> $DIR/deny-async.rs:10:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn callee() { + | --------------- `callee` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: `callee_justified` is incompatible with `#[rustc_force_inline]` + --> $DIR/deny-async.rs:16:1 + | +LL | #[rustc_force_inline = "the test requires it"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn callee_justified() { + | ------------------------- `callee_justified` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: aborting due to 2 previous errors + diff --git a/tests/ui/force-inlining/deny-closure.rs b/tests/ui/force-inlining/deny-closure.rs new file mode 100644 index 000000000000..31314c450fcf --- /dev/null +++ b/tests/ui/force-inlining/deny-closure.rs @@ -0,0 +1,25 @@ +//@ check-fail +//@ compile-flags: --crate-type=lib +#![allow(internal_features)] +#![feature(rustc_attrs)] + +// Test that forced inlining into closures w/ errors works as expected. + +#[rustc_no_mir_inline] +#[rustc_force_inline] +//~^ ERROR `callee` is incompatible with `#[rustc_force_inline]` +pub fn callee() { +} + +#[rustc_no_mir_inline] +#[rustc_force_inline = "the test requires it"] +//~^ ERROR `callee_justified` is incompatible with `#[rustc_force_inline]` +pub fn callee_justified() { +} + +pub fn caller() { + (|| { + callee(); + callee_justified(); + })(); +} diff --git a/tests/ui/force-inlining/deny-closure.stderr b/tests/ui/force-inlining/deny-closure.stderr new file mode 100644 index 000000000000..e657a2954202 --- /dev/null +++ b/tests/ui/force-inlining/deny-closure.stderr @@ -0,0 +1,24 @@ +error: `callee` is incompatible with `#[rustc_force_inline]` + --> $DIR/deny-closure.rs:9:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn callee() { + | --------------- `callee` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: `callee_justified` is incompatible with `#[rustc_force_inline]` + --> $DIR/deny-closure.rs:15:1 + | +LL | #[rustc_force_inline = "the test requires it"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn callee_justified() { + | ------------------------- `callee_justified` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: aborting due to 2 previous errors + diff --git a/tests/ui/force-inlining/deny.rs b/tests/ui/force-inlining/deny.rs new file mode 100644 index 000000000000..7712f5f50f31 --- /dev/null +++ b/tests/ui/force-inlining/deny.rs @@ -0,0 +1,23 @@ +//@ check-fail +//@ compile-flags: --crate-type=lib +#![allow(internal_features)] +#![feature(rustc_attrs)] + +// Test that forced inlining w/ errors works as expected. + +#[rustc_no_mir_inline] +#[rustc_force_inline] +//~^ ERROR `callee` is incompatible with `#[rustc_force_inline]` +pub fn callee() { +} + +#[rustc_no_mir_inline] +#[rustc_force_inline = "the test requires it"] +//~^ ERROR `callee_justified` is incompatible with `#[rustc_force_inline]` +pub fn callee_justified() { +} + +pub fn caller() { + callee(); + callee_justified(); +} diff --git a/tests/ui/force-inlining/deny.stderr b/tests/ui/force-inlining/deny.stderr new file mode 100644 index 000000000000..c276fa28ba89 --- /dev/null +++ b/tests/ui/force-inlining/deny.stderr @@ -0,0 +1,24 @@ +error: `callee` is incompatible with `#[rustc_force_inline]` + --> $DIR/deny.rs:9:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn callee() { + | --------------- `callee` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: `callee_justified` is incompatible with `#[rustc_force_inline]` + --> $DIR/deny.rs:15:1 + | +LL | #[rustc_force_inline = "the test requires it"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn callee_justified() { + | ------------------------- `callee_justified` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: aborting due to 2 previous errors + diff --git a/tests/ui/force-inlining/early-deny.rs b/tests/ui/force-inlining/early-deny.rs new file mode 100644 index 000000000000..99b03a4e0e2c --- /dev/null +++ b/tests/ui/force-inlining/early-deny.rs @@ -0,0 +1,21 @@ +//@ check-fail +//@ compile-flags: --crate-type=lib +#![feature(c_variadic)] +#![feature(rustc_attrs)] + +#[rustc_no_mir_inline] +#[rustc_force_inline] +//~^ ERROR `rustc_attr` is incompatible with `#[rustc_force_inline]` +pub fn rustc_attr() { +} + +#[cold] +#[rustc_force_inline] +//~^ ERROR `cold` is incompatible with `#[rustc_force_inline]` +pub fn cold() { +} + +#[rustc_force_inline] +//~^ ERROR `variadic` is incompatible with `#[rustc_force_inline]` +pub unsafe extern "C" fn variadic(args: ...) { +} diff --git a/tests/ui/force-inlining/early-deny.stderr b/tests/ui/force-inlining/early-deny.stderr new file mode 100644 index 000000000000..abee66fd293c --- /dev/null +++ b/tests/ui/force-inlining/early-deny.stderr @@ -0,0 +1,35 @@ +error: `rustc_attr` is incompatible with `#[rustc_force_inline]` + --> $DIR/early-deny.rs:7:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn rustc_attr() { + | ------------------- `rustc_attr` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: `cold` is incompatible with `#[rustc_force_inline]` + --> $DIR/early-deny.rs:13:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn cold() { + | ------------- `cold` defined here + | + = note: incompatible due to: cold + +error: `variadic` is incompatible with `#[rustc_force_inline]` + --> $DIR/early-deny.rs:18:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub unsafe extern "C" fn variadic(args: ...) { + | -------------------------------------------- `variadic` defined here + | + = note: incompatible due to: C variadic + +error: aborting due to 3 previous errors + diff --git a/tests/ui/force-inlining/gate.rs b/tests/ui/force-inlining/gate.rs new file mode 100644 index 000000000000..d6a01a74a44b --- /dev/null +++ b/tests/ui/force-inlining/gate.rs @@ -0,0 +1,12 @@ +//@ compile-flags: --crate-type=lib +#![allow(internal_features)] + +#[rustc_force_inline] +//~^ ERROR #![rustc_force_inline] forces a free function to be inlined +pub fn bare() { +} + +#[rustc_force_inline = "the test requires it"] +//~^ ERROR #![rustc_force_inline] forces a free function to be inlined +pub fn justified() { +} diff --git a/tests/ui/force-inlining/gate.stderr b/tests/ui/force-inlining/gate.stderr new file mode 100644 index 000000000000..e3973a08c232 --- /dev/null +++ b/tests/ui/force-inlining/gate.stderr @@ -0,0 +1,21 @@ +error[E0658]: #![rustc_force_inline] forces a free function to be inlined + --> $DIR/gate.rs:4:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` 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]: #![rustc_force_inline] forces a free function to be inlined + --> $DIR/gate.rs:9:1 + | +LL | #[rustc_force_inline = "the test requires it"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` 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 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/force-inlining/invalid.rs b/tests/ui/force-inlining/invalid.rs new file mode 100644 index 000000000000..7574078b245c --- /dev/null +++ b/tests/ui/force-inlining/invalid.rs @@ -0,0 +1,164 @@ +//@ edition: 2024 +#![allow(internal_features, unused_imports, unused_macros)] +#![feature(extern_types)] +#![feature(gen_blocks)] +#![feature(rustc_attrs)] +#![feature(stmt_expr_attributes)] +#![feature(trait_alias)] + +// Test that invalid force inlining attributes error as expected. + +#[rustc_force_inline("foo")] +//~^ ERROR malformed `rustc_force_inline` attribute input +pub fn forced1() { +} + +#[rustc_force_inline(bar, baz)] +//~^ ERROR malformed `rustc_force_inline` attribute input +pub fn forced2() { +} + +#[rustc_force_inline(2)] +//~^ ERROR malformed `rustc_force_inline` attribute input +pub fn forced3() { +} + +#[rustc_force_inline = 2] +//~^ ERROR malformed `rustc_force_inline` attribute input +pub fn forced4() { +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +extern crate std as other_std; + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +use std::collections::HashMap; + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +static _FOO: &'static str = "FOO"; + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +const _BAR: u32 = 3; + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +mod foo { } + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +unsafe extern "C" { + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + static X: &'static u32; + + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + type Y; + + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + fn foo(); +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +type Foo = u32; + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +enum Bar<#[rustc_force_inline] T> { +//~^ ERROR attribute should be applied to a function + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + Baz(std::marker::PhantomData), +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +struct Qux { + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + field: u32, +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +union FooBar { + x: u32, + y: u32, +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +trait FooBaz { + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + type Foo; + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + const Bar: i32; + + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + fn foo() {} +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +trait FooQux = FooBaz; + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +impl Bar { + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + fn foo() {} +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +impl FooBaz for Bar { + type Foo = u32; + const Bar: i32 = 3; +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +macro_rules! barqux { ($foo:tt) => { $foo }; } + +fn barqux(#[rustc_force_inline] _x: u32) {} +//~^ ERROR allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters +//~^^ ERROR attribute should be applied to a function + +#[rustc_force_inline] +//~^ ERROR attribute cannot be applied to a `async`, `gen` or `async gen` function +async fn async_foo() {} + +#[rustc_force_inline] +//~^ ERROR attribute cannot be applied to a `async`, `gen` or `async gen` function +gen fn gen_foo() {} + +#[rustc_force_inline] +//~^ ERROR attribute cannot be applied to a `async`, `gen` or `async gen` function +async gen fn async_gen_foo() {} + +fn main() { + let _x = #[rustc_force_inline] || { }; +//~^ ERROR attribute should be applied to a function + let _y = #[rustc_force_inline] 3 + 4; +//~^ ERROR attribute should be applied to a function + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + let _z = 3; + + match _z { + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + 1 => (), + _ => (), + } +} diff --git a/tests/ui/force-inlining/invalid.stderr b/tests/ui/force-inlining/invalid.stderr new file mode 100644 index 000000000000..5d280124129b --- /dev/null +++ b/tests/ui/force-inlining/invalid.stderr @@ -0,0 +1,377 @@ +error: malformed `rustc_force_inline` attribute input + --> $DIR/invalid.rs:11:1 + | +LL | #[rustc_force_inline("foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[rustc_force_inline = "reason"] + | +LL | #[rustc_force_inline] + | + +error: malformed `rustc_force_inline` attribute input + --> $DIR/invalid.rs:16:1 + | +LL | #[rustc_force_inline(bar, baz)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[rustc_force_inline = "reason"] + | +LL | #[rustc_force_inline] + | + +error: malformed `rustc_force_inline` attribute input + --> $DIR/invalid.rs:21:1 + | +LL | #[rustc_force_inline(2)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[rustc_force_inline = "reason"] + | +LL | #[rustc_force_inline] + | + +error: malformed `rustc_force_inline` attribute input + --> $DIR/invalid.rs:26:1 + | +LL | #[rustc_force_inline = 2] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[rustc_force_inline = "reason"] + | +LL | #[rustc_force_inline] + | + +error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters + --> $DIR/invalid.rs:133:11 + | +LL | fn barqux(#[rustc_force_inline] _x: u32) {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error: attribute should be applied to a function + --> $DIR/invalid.rs:31:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | extern crate std as other_std; + | ------------------------------ not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:35:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | use std::collections::HashMap; + | ------------------------------ not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:39:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | static _FOO: &'static str = "FOO"; + | ---------------------------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:43:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | const _BAR: u32 = 3; + | -------------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:47:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | mod foo { } + | ----------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:51:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / unsafe extern "C" { +LL | | #[rustc_force_inline] +LL | | +LL | | static X: &'static u32; +... | +LL | | fn foo(); +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:67:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | type Foo = u32; + | --------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:71:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / enum Bar<#[rustc_force_inline] T> { +LL | | +LL | | #[rustc_force_inline] +... | +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:73:10 + | +LL | enum Bar<#[rustc_force_inline] T> { + | ^^^^^^^^^^^^^^^^^^^^^ - not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:75:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | Baz(std::marker::PhantomData), + | -------------------------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:80:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / struct Qux { +LL | | #[rustc_force_inline] +LL | | +LL | | field: u32, +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:83:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | field: u32, + | ---------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:88:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / union FooBar { +LL | | x: u32, +LL | | y: u32, +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:95:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / trait FooBaz { +LL | | #[rustc_force_inline] +LL | | +LL | | type Foo; +... | +LL | | fn foo() {} +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:110:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | trait FooQux = FooBaz; + | ---------------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:114:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / impl Bar { +LL | | #[rustc_force_inline] +LL | | +LL | | fn foo() {} +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:122:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / impl FooBaz for Bar { +LL | | type Foo = u32; +LL | | const Bar: i32 = 3; +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:129:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | macro_rules! barqux { ($foo:tt) => { $foo }; } + | ---------------------------------------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:133:11 + | +LL | fn barqux(#[rustc_force_inline] _x: u32) {} + | ^^^^^^^^^^^^^^^^^^^^^-------- + | | + | not a function definition + +error: attribute cannot be applied to a `async`, `gen` or `async gen` function + --> $DIR/invalid.rs:137:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | async fn async_foo() {} + | -------------------- `async`, `gen` or `async gen` function + +error: attribute cannot be applied to a `async`, `gen` or `async gen` function + --> $DIR/invalid.rs:141:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | gen fn gen_foo() {} + | ---------------- `async`, `gen` or `async gen` function + +error: attribute cannot be applied to a `async`, `gen` or `async gen` function + --> $DIR/invalid.rs:145:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | async gen fn async_gen_foo() {} + | ---------------------------- `async`, `gen` or `async gen` function + +error: attribute should be applied to a function + --> $DIR/invalid.rs:150:14 + | +LL | let _x = #[rustc_force_inline] || { }; + | ^^^^^^^^^^^^^^^^^^^^^ ------ not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:152:14 + | +LL | let _y = #[rustc_force_inline] 3 + 4; + | ^^^^^^^^^^^^^^^^^^^^^ - not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:154:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | let _z = 3; + | ----------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:159:9 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | 1 => (), + | ------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:98:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | type Foo; + | --------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:101:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | const Bar: i32; + | --------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:105:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn foo() {} + | ----------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:117:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn foo() {} + | ----------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:54:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | static X: &'static u32; + | ----------------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:58:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | type Y; + | ------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:62:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn foo(); + | --------- not a function definition + +error: aborting due to 38 previous errors + diff --git a/tests/ui/force-inlining/shims.rs b/tests/ui/force-inlining/shims.rs new file mode 100644 index 000000000000..03b7d07cda39 --- /dev/null +++ b/tests/ui/force-inlining/shims.rs @@ -0,0 +1,9 @@ +//@ build-pass +#![allow(internal_features)] +#![feature(rustc_attrs)] + +#[rustc_force_inline] +fn f() {} +fn g(t: T) { t(); } + +fn main() { g(f); } diff --git a/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.edition2021.stderr b/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.edition2021.stderr index 1e268bc7f49b..112341287f8a 100644 --- a/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.edition2021.stderr +++ b/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.edition2021.stderr @@ -9,6 +9,18 @@ help: you can add the `dyn` keyword if you want a trait object LL | fn ice() -> impl AsRef { | +++ -error: aborting due to 1 previous error +error[E0782]: expected a type, found a trait + --> $DIR/generic-with-implicit-hrtb-without-dyn.rs:6:24 + | +LL | fn ice() -> impl AsRef { + | ^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: you can add the `dyn` keyword if you want a trait object + | +LL | fn ice() -> impl AsRef { + | +++ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.rs b/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.rs index c71f794d5d12..df43250f1515 100644 --- a/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.rs +++ b/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.rs @@ -6,6 +6,7 @@ fn ice() -> impl AsRef { //[edition2015]~^ ERROR: the trait bound `(): AsRef<(dyn for<'a> Fn(&'a ()) + 'static)>` is not satisfied [E0277] //[edition2021]~^^ ERROR: expected a type, found a trait [E0782] + //[edition2021]~| ERROR: expected a type, found a trait [E0782] todo!() } diff --git a/tests/ui/impl-trait/issue-99073-2.stderr b/tests/ui/impl-trait/issue-99073-2.stderr index 06b2b84569f6..0bcac7c7c534 100644 --- a/tests/ui/impl-trait/issue-99073-2.stderr +++ b/tests/ui/impl-trait/issue-99073-2.stderr @@ -1,12 +1,3 @@ -error[E0792]: expected generic type parameter, found `i32` - --> $DIR/issue-99073-2.rs:9:22 - | -LL | fn test(t: T, recurse: bool) -> impl Display { - | - this generic parameter must be used with a generic type parameter -LL | let f = || { -LL | let i: u32 = test::(-1, false); - | ^^^^^^^^^^^^^^^^^^^^^^ - error: concrete type differs from previous defining opaque type use --> $DIR/issue-99073-2.rs:9:22 | @@ -19,6 +10,15 @@ note: previous use here LL | fn test(t: T, recurse: bool) -> impl Display { | ^^^^^^^^^^^^ +error[E0792]: expected generic type parameter, found `i32` + --> $DIR/issue-99073-2.rs:9:22 + | +LL | fn test(t: T, recurse: bool) -> impl Display { + | - this generic parameter must be used with a generic type parameter +LL | let f = || { +LL | let i: u32 = test::(-1, false); + | ^^^^^^^^^^^^^^^^^^^^^^ + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/issue-99073.stderr b/tests/ui/impl-trait/issue-99073.stderr index 3c32f1794a0e..19854ef89405 100644 --- a/tests/ui/impl-trait/issue-99073.stderr +++ b/tests/ui/impl-trait/issue-99073.stderr @@ -1,11 +1,3 @@ -error[E0792]: expected generic type parameter, found `&F` - --> $DIR/issue-99073.rs:6:11 - | -LL | fn fix(f: F) -> impl Fn() { - | - this generic parameter must be used with a generic type parameter -LL | move || f(fix(&f)) - | ^^^^^^^^^^ - error: concrete type differs from previous defining opaque type use --> $DIR/issue-99073.rs:6:13 | @@ -18,6 +10,14 @@ note: previous use here LL | fn fix(f: F) -> impl Fn() { | ^^^^^^^^^ +error[E0792]: expected generic type parameter, found `&F` + --> $DIR/issue-99073.rs:6:11 + | +LL | fn fix(f: F) -> impl Fn() { + | - this generic parameter must be used with a generic type parameter +LL | move || f(fix(&f)) + | ^^^^^^^^^^ + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/multiple-defining-usages-in-body.stderr b/tests/ui/impl-trait/multiple-defining-usages-in-body.stderr index faa5d3ba4489..aaa1958b5847 100644 --- a/tests/ui/impl-trait/multiple-defining-usages-in-body.stderr +++ b/tests/ui/impl-trait/multiple-defining-usages-in-body.stderr @@ -1,15 +1,3 @@ -warning: function cannot return without recursing - --> $DIR/multiple-defining-usages-in-body.rs:4:1 - | -LL | fn foo() -> impl Trait { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing -LL | -LL | let a: T = foo::(); - | ------------- recursive call site - | - = help: a `loop` may express intention better if this is on purpose - = note: `#[warn(unconditional_recursion)]` on by default - error: concrete type differs from previous defining opaque type use --> $DIR/multiple-defining-usages-in-body.rs:8:16 | @@ -22,5 +10,17 @@ note: previous use here LL | let a: T = foo::(); | ^^^^^^^^^^^^^ +warning: function cannot return without recursing + --> $DIR/multiple-defining-usages-in-body.rs:4:1 + | +LL | fn foo() -> impl Trait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | let a: T = foo::(); + | ------------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + error: aborting due to 1 previous error; 1 warning emitted 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/normalize-tait-in-const.stderr b/tests/ui/impl-trait/normalize-tait-in-const.stderr index 0f79cefeaecb..f4e8a872cec1 100644 --- a/tests/ui/impl-trait/normalize-tait-in-const.stderr +++ b/tests/ui/impl-trait/normalize-tait-in-const.stderr @@ -17,20 +17,6 @@ note: `Fn` can't be used with `~const` because it isn't annotated with `#[const_ --> $SRC_DIR/core/src/ops/function.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: the trait bound `for<'a, 'b> fn(&'a foo::Alias<'b>) {foo}: const Destruct` is not satisfied - --> $DIR/normalize-tait-in-const.rs:33:19 - | -LL | with_positive(foo); - | ------------- ^^^ - | | - | required by a bound introduced by this call - | -note: required by a bound in `with_positive` - --> $DIR/normalize-tait-in-const.rs:26:62 - | -LL | const fn with_positive ~const Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { - | ^^^^^^ required by this bound in `with_positive` - error[E0015]: cannot call non-const closure in constant functions --> $DIR/normalize-tait-in-const.rs:27:5 | @@ -39,7 +25,6 @@ LL | fun(filter_positive()); | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0015, E0277. -For more information about an error, try `rustc --explain E0015`. +For more information about this error, try `rustc --explain E0015`. 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/impl-trait/rpit/early_bound.stderr b/tests/ui/impl-trait/rpit/early_bound.stderr index b0c7bd4199cc..230dde95764b 100644 --- a/tests/ui/impl-trait/rpit/early_bound.stderr +++ b/tests/ui/impl-trait/rpit/early_bound.stderr @@ -1,12 +1,3 @@ -error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/early_bound.rs:7:17 - | -LL | fn test<'a: 'a>(n: bool) -> impl Sized + 'a { - | -- this generic parameter must be used with a generic lifetime parameter -... -LL | let _ = identity::<&'a ()>(test(false)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: concrete type differs from previous defining opaque type use --> $DIR/early_bound.rs:3:29 | @@ -19,6 +10,15 @@ note: previous use here LL | let _ = identity::<&'a ()>(test(false)); | ^^^^^^^^^^^ +error[E0792]: expected generic lifetime parameter, found `'_` + --> $DIR/early_bound.rs:7:17 + | +LL | fn test<'a: 'a>(n: bool) -> impl Sized + 'a { + | -- this generic parameter must be used with a generic lifetime parameter +... +LL | let _ = identity::<&'a ()>(test(false)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/rpit/non-defining-use.stderr b/tests/ui/impl-trait/rpit/non-defining-use.stderr index 19987d47672b..10a8232e646d 100644 --- a/tests/ui/impl-trait/rpit/non-defining-use.stderr +++ b/tests/ui/impl-trait/rpit/non-defining-use.stderr @@ -6,14 +6,6 @@ LL | fn foo() -> impl Sized { LL | let _: () = foo::(); | ^^ -error[E0792]: expected generic type parameter, found `u8` - --> $DIR/non-defining-use.rs:8:12 - | -LL | fn bar(val: T) -> impl Sized { - | - this generic parameter must be used with a generic type parameter -LL | let _: u8 = bar(0u8); - | ^^ - error: concrete type differs from previous defining opaque type use --> $DIR/non-defining-use.rs:8:17 | @@ -26,6 +18,14 @@ note: previous use here LL | fn bar(val: T) -> impl Sized { | ^^^^^^^^^^ +error[E0792]: expected generic type parameter, found `u8` + --> $DIR/non-defining-use.rs:8:12 + | +LL | fn bar(val: T) -> impl Sized { + | - this generic parameter must be used with a generic type parameter +LL | let _: u8 = bar(0u8); + | ^^ + error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/inline-const/collect-scopes-in-pat.rs b/tests/ui/inline-const/collect-scopes-in-pat.rs new file mode 100644 index 000000000000..024fde537413 --- /dev/null +++ b/tests/ui/inline-const/collect-scopes-in-pat.rs @@ -0,0 +1,16 @@ +// @compile-flags: -Zlint-mir +//@ check-pass + +#![feature(inline_const_pat)] + +fn main() { + match 1 { + const { + || match 0 { + x => 0, + }; + 0 + } => (), + _ => (), + } +} 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/invalid-compile-flags/crate-type-flag.rs b/tests/ui/invalid-compile-flags/crate-type-flag.rs index 42bd72cbfbf5..bc7a0bc46c3a 100644 --- a/tests/ui/invalid-compile-flags/crate-type-flag.rs +++ b/tests/ui/invalid-compile-flags/crate-type-flag.rs @@ -30,6 +30,7 @@ //@[bin] check-pass //@[proc_dash_macro] ignore-wasm (proc-macro is not supported) +//@[proc_dash_macro] needs-unwind (panic=abort causes warning to be emitted) //@[proc_dash_macro] compile-flags: --crate-type=proc-macro //@[proc_dash_macro] check-pass 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-25901.rs b/tests/ui/issues/issue-25901.rs index eae038c71a0e..bfcee1ac503a 100644 --- a/tests/ui/issues/issue-25901.rs +++ b/tests/ui/issues/issue-25901.rs @@ -2,7 +2,7 @@ struct A; struct B; static S: &'static B = &A; -//~^ ERROR cannot call conditionally-const method +//~^ ERROR cannot perform conditionally-const deref coercion use std::ops::Deref; diff --git a/tests/ui/issues/issue-25901.stderr b/tests/ui/issues/issue-25901.stderr index 655a8b78c6a1..a954f38af839 100644 --- a/tests/ui/issues/issue-25901.stderr +++ b/tests/ui/issues/issue-25901.stderr @@ -1,9 +1,16 @@ -error[E0658]: cannot call conditionally-const method `::deref` in statics +error[E0658]: cannot perform conditionally-const deref coercion on `A` in statics --> $DIR/issue-25901.rs:4:24 | LL | static S: &'static B = &A; | ^^ | + = note: attempting to deref into `B` +note: deref defined here + --> $DIR/issue-25901.rs:10:5 + | +LL | type Target = B; + | ^^^^^^^^^^^ + = note: calls in statics are limited to constant functions, tuple structs and tuple variants = note: see issue #67792 for more information = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date 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/layout/debug.rs b/tests/ui/layout/debug.rs index 5602c4e711f6..81dc72852543 100644 --- a/tests/ui/layout/debug.rs +++ b/tests/ui/layout/debug.rs @@ -1,4 +1,5 @@ //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$SOME_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" #![feature(never_type, rustc_attrs, type_alias_impl_trait, repr_simd)] #![crate_type = "lib"] diff --git a/tests/ui/layout/debug.stderr b/tests/ui/layout/debug.stderr index bd31665dac1f..1fc55511384f 100644 --- a/tests/ui/layout/debug.stderr +++ b/tests/ui/layout/debug.stderr @@ -1,5 +1,5 @@ error: unions cannot have zero fields - --> $DIR/debug.rs:82:1 + --> $DIR/debug.rs:83:1 | LL | union EmptyUnion {} | ^^^^^^^^^^^^^^^^^^^ @@ -61,6 +61,7 @@ error: layout_of(E) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(12 bytes), @@ -87,13 +88,15 @@ error: layout_of(E) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:7:1 + --> $DIR/debug.rs:8:1 | LL | enum E { Foo, Bar(!, i32, i32) } | ^^^^^^ @@ -138,8 +141,9 @@ error: layout_of(S) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:10:1 + --> $DIR/debug.rs:11:1 | LL | struct S { f1: i32, f2: (), f3: i32 } | ^^^^^^^^ @@ -162,8 +166,9 @@ error: layout_of(U) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:13:1 + --> $DIR/debug.rs:14:1 | LL | union U { f1: (i32, i32), f3: i32 } | ^^^^^^^ @@ -255,6 +260,7 @@ error: layout_of(Result) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(8 bytes), @@ -292,13 +298,15 @@ error: layout_of(Result) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:16:1 + --> $DIR/debug.rs:17:1 | LL | type Test = Result; | ^^^^^^^^^ @@ -325,8 +333,9 @@ error: layout_of(i32) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:19:1 + --> $DIR/debug.rs:20:1 | LL | type T = impl std::fmt::Debug; | ^^^^^^ @@ -349,8 +358,9 @@ error: layout_of(V) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:25:1 + --> $DIR/debug.rs:26:1 | LL | pub union V { | ^^^^^^^^^^^ @@ -373,8 +383,9 @@ error: layout_of(W) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:31:1 + --> $DIR/debug.rs:32:1 | LL | pub union W { | ^^^^^^^^^^^ @@ -397,8 +408,9 @@ error: layout_of(Y) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:37:1 + --> $DIR/debug.rs:38:1 | LL | pub union Y { | ^^^^^^^^^^^ @@ -421,8 +433,9 @@ error: layout_of(P1) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:44:1 + --> $DIR/debug.rs:45:1 | LL | union P1 { x: u32 } | ^^^^^^^^ @@ -445,8 +458,9 @@ error: layout_of(P2) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:48:1 + --> $DIR/debug.rs:49:1 | LL | union P2 { x: (u32, u32) } | ^^^^^^^^ @@ -469,8 +483,9 @@ error: layout_of(P3) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:56:1 + --> $DIR/debug.rs:57:1 | LL | union P3 { x: F32x4 } | ^^^^^^^^ @@ -493,8 +508,9 @@ error: layout_of(P4) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:60:1 + --> $DIR/debug.rs:61:1 | LL | union P4 { x: E } | ^^^^^^^^ @@ -522,8 +538,9 @@ error: layout_of(P5) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:64:1 + --> $DIR/debug.rs:65:1 | LL | union P5 { zst: [u16; 0], byte: u8 } | ^^^^^^^^ @@ -551,20 +568,21 @@ error: layout_of(MaybeUninit) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:67:1 + --> $DIR/debug.rs:68:1 | LL | type X = std::mem::MaybeUninit; | ^^^^^^ error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases - --> $DIR/debug.rs:70:1 + --> $DIR/debug.rs:71:1 | LL | const C: () = (); | ^^^^^^^^^^^ error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/debug.rs:78:19 + --> $DIR/debug.rs:79:19 | LL | type Impossible = (str, str); | ^^^^^^^^^^ doesn't have a size known at compile-time @@ -573,13 +591,13 @@ LL | type Impossible = (str, str); = note: only the last element of a tuple may have a dynamically sized type error: the type `EmptyUnion` has an unknown layout - --> $DIR/debug.rs:82:1 + --> $DIR/debug.rs:83:1 | LL | union EmptyUnion {} | ^^^^^^^^^^^^^^^^ error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases - --> $DIR/debug.rs:74:5 + --> $DIR/debug.rs:75:5 | LL | const C: () = (); | ^^^^^^^^^^^ diff --git a/tests/ui/layout/hexagon-enum.rs b/tests/ui/layout/hexagon-enum.rs index e3a5c53671d4..5fa12e479e7d 100644 --- a/tests/ui/layout/hexagon-enum.rs +++ b/tests/ui/layout/hexagon-enum.rs @@ -1,4 +1,5 @@ //@ compile-flags: --target hexagon-unknown-linux-musl +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" //@ needs-llvm-components: hexagon // // Verify that the hexagon targets implement the repr(C) for enums correctly. diff --git a/tests/ui/layout/hexagon-enum.stderr b/tests/ui/layout/hexagon-enum.stderr index 59fe667923f1..96f0a8c87408 100644 --- a/tests/ui/layout/hexagon-enum.stderr +++ b/tests/ui/layout/hexagon-enum.stderr @@ -61,13 +61,15 @@ error: layout_of(A) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/hexagon-enum.rs:16:1 + --> $DIR/hexagon-enum.rs:17:1 | LL | enum A { Apple } | ^^^^^^ @@ -135,13 +137,15 @@ error: layout_of(B) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/hexagon-enum.rs:20:1 + --> $DIR/hexagon-enum.rs:21:1 | LL | enum B { Banana = 255, } | ^^^^^^ @@ -209,13 +213,15 @@ error: layout_of(C) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, } - --> $DIR/hexagon-enum.rs:24:1 + --> $DIR/hexagon-enum.rs:25:1 | LL | enum C { Chaenomeles = 256, } | ^^^^^^ @@ -283,13 +289,15 @@ error: layout_of(P) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/hexagon-enum.rs:28:1 + --> $DIR/hexagon-enum.rs:29:1 | LL | enum P { Peach = 0x1000_0000isize, } | ^^^^^^ @@ -357,13 +365,15 @@ error: layout_of(T) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/hexagon-enum.rs:34:1 + --> $DIR/hexagon-enum.rs:35:1 | LL | enum T { Tangerine = TANGERINE as isize } | ^^^^^^ diff --git a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs index 328d204aa3cd..ab7e0897ce32 100644 --- a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs +++ b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs @@ -1,4 +1,5 @@ //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" #![crate_type = "lib"] #![feature(rustc_attrs)] diff --git a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr index ca041fb539b9..cd9e4c027815 100644 --- a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr +++ b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr @@ -83,6 +83,7 @@ error: layout_of(MissingPayloadField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(1 bytes), @@ -103,13 +104,15 @@ error: layout_of(MissingPayloadField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:16:1 + --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:17:1 | LL | pub enum MissingPayloadField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -201,6 +204,7 @@ error: layout_of(CommonPayloadField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -238,13 +242,15 @@ error: layout_of(CommonPayloadField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:25:1 + --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:26:1 | LL | pub enum CommonPayloadField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -334,6 +340,7 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -370,13 +377,15 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:33:1 + --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:34:1 | LL | pub enum CommonPayloadFieldIsMaybeUninit { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -482,6 +491,7 @@ error: layout_of(NicheFirst) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(0 bytes), @@ -502,6 +512,7 @@ error: layout_of(NicheFirst) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(0 bytes), @@ -522,13 +533,15 @@ error: layout_of(NicheFirst) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:41:1 + --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:42:1 | LL | pub enum NicheFirst { | ^^^^^^^^^^^^^^^^^^^ @@ -634,6 +647,7 @@ error: layout_of(NicheSecond) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(0 bytes), @@ -654,6 +668,7 @@ error: layout_of(NicheSecond) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(0 bytes), @@ -674,13 +689,15 @@ error: layout_of(NicheSecond) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:50:1 + --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:51:1 | LL | pub enum NicheSecond { | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/layout/issue-96185-overaligned-enum.rs b/tests/ui/layout/issue-96185-overaligned-enum.rs index 341233a7890e..19da169105ce 100644 --- a/tests/ui/layout/issue-96185-overaligned-enum.rs +++ b/tests/ui/layout/issue-96185-overaligned-enum.rs @@ -1,4 +1,5 @@ //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" #![crate_type = "lib"] #![feature(rustc_attrs)] diff --git a/tests/ui/layout/issue-96185-overaligned-enum.stderr b/tests/ui/layout/issue-96185-overaligned-enum.stderr index bc40a2aa482e..15a3f6004f50 100644 --- a/tests/ui/layout/issue-96185-overaligned-enum.stderr +++ b/tests/ui/layout/issue-96185-overaligned-enum.stderr @@ -57,6 +57,7 @@ error: layout_of(Aligned1) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(8 bytes), @@ -79,6 +80,7 @@ error: layout_of(Aligned1) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, @@ -86,8 +88,9 @@ error: layout_of(Aligned1) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96185-overaligned-enum.rs:8:1 + --> $DIR/issue-96185-overaligned-enum.rs:9:1 | LL | pub enum Aligned1 { | ^^^^^^^^^^^^^^^^^ @@ -157,6 +160,7 @@ error: layout_of(Aligned2) = Layout { Align(1 bytes), ), unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(1 bytes), @@ -179,6 +183,7 @@ error: layout_of(Aligned2) = Layout { Align(1 bytes), ), unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, @@ -186,8 +191,9 @@ error: layout_of(Aligned2) = Layout { Align(1 bytes), ), unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96185-overaligned-enum.rs:16:1 + --> $DIR/issue-96185-overaligned-enum.rs:17:1 | LL | pub enum Aligned2 { | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/layout/randomize.rs b/tests/ui/layout/randomize.rs new file mode 100644 index 000000000000..27e99327a319 --- /dev/null +++ b/tests/ui/layout/randomize.rs @@ -0,0 +1,62 @@ +//@ run-pass +//@ revisions: normal randomize-layout +//@ [randomize-layout]compile-flags: -Zrandomize-layout -Zlayout-seed=2 + +#![feature(offset_of_enum)] + +use std::ptr; + + +// these types only have their field offsets taken, they're never constructed +#[allow(dead_code)] +pub struct Foo(u32, T, u8); +#[allow(dead_code)] +pub struct Wrapper(T); +#[repr(transparent)] +#[allow(dead_code)] +pub struct TransparentWrapper(u16); + +const _: () = { + // Behavior of the current non-randomized implementation, not guaranteed + #[cfg(not(randomize_layout))] + assert!(std::mem::offset_of!(Foo::, 1) == std::mem::offset_of!(Foo::>, 1)); + + // under randomization Foo != Foo + #[cfg(randomize_layout)] + assert!(std::mem::offset_of!(Foo::, 1) != std::mem::offset_of!(Foo::>, 1)); + + // Even transparent wrapper inner types get a different layout since associated type + // specialization could result in the outer type behaving differently depending on the exact + // inner type. + #[cfg(randomize_layout)] + assert!( + std::mem::offset_of!(Foo::, 1) != std::mem::offset_of!(Foo::, 1) + ); + + // Currently all fn pointers are treated interchangably even with randomization. Not guaranteed. + // Associated type specialization could also break this. + assert!( + std::mem::offset_of!(Foo::, 1) == std::mem::offset_of!(Foo:: usize>, 1) + ); + + // But subtype coercions must always result in the same layout. + assert!( + std::mem::offset_of!(Foo::, 1) == std::mem::offset_of!(Foo::, 1) + ); + + // Randomization must uphold NPO guarantees + assert!(std::mem::offset_of!(Option::<&usize>, Some.0) == 0); + assert!(std::mem::offset_of!(Result::<&usize, ()>, Ok.0) == 0); +}; + +#[allow(dead_code)] +struct Unsizable(usize, T); + +fn main() { + // offset_of doesn't let us probe the unsized field, check at runtime. + let x = &Unsizable::<[u32; 4]>(0, [0; 4]); + let y: &Unsizable::<[u32]> = x; + + // type coercion must not change the layout. + assert_eq!(ptr::from_ref(&x.1).addr(), ptr::from_ref(&y.1).addr()); +} diff --git a/tests/ui/layout/thumb-enum.rs b/tests/ui/layout/thumb-enum.rs index 57a9a2d81370..2381d9d02926 100644 --- a/tests/ui/layout/thumb-enum.rs +++ b/tests/ui/layout/thumb-enum.rs @@ -1,4 +1,5 @@ //@ compile-flags: --target thumbv8m.main-none-eabihf +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" //@ needs-llvm-components: arm // // Verify that thumb targets implement the repr(C) for enums correctly. diff --git a/tests/ui/layout/thumb-enum.stderr b/tests/ui/layout/thumb-enum.stderr index bf043af586b1..120081d193c6 100644 --- a/tests/ui/layout/thumb-enum.stderr +++ b/tests/ui/layout/thumb-enum.stderr @@ -61,13 +61,15 @@ error: layout_of(A) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/thumb-enum.rs:16:1 + --> $DIR/thumb-enum.rs:17:1 | LL | enum A { Apple } | ^^^^^^ @@ -135,13 +137,15 @@ error: layout_of(B) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/thumb-enum.rs:20:1 + --> $DIR/thumb-enum.rs:21:1 | LL | enum B { Banana = 255, } | ^^^^^^ @@ -209,13 +213,15 @@ error: layout_of(C) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, } - --> $DIR/thumb-enum.rs:24:1 + --> $DIR/thumb-enum.rs:25:1 | LL | enum C { Chaenomeles = 256, } | ^^^^^^ @@ -283,13 +289,15 @@ error: layout_of(P) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/thumb-enum.rs:28:1 + --> $DIR/thumb-enum.rs:29:1 | LL | enum P { Peach = 0x1000_0000isize, } | ^^^^^^ @@ -357,13 +365,15 @@ error: layout_of(T) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/thumb-enum.rs:34:1 + --> $DIR/thumb-enum.rs:35:1 | LL | enum T { Tangerine = TANGERINE as isize } | ^^^^^^ diff --git a/tests/ui/layout/zero-sized-array-enum-niche.rs b/tests/ui/layout/zero-sized-array-enum-niche.rs index 152f44bd8637..d3ff016d8aa2 100644 --- a/tests/ui/layout/zero-sized-array-enum-niche.rs +++ b/tests/ui/layout/zero-sized-array-enum-niche.rs @@ -1,4 +1,5 @@ //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" #![crate_type = "lib"] #![feature(rustc_attrs)] diff --git a/tests/ui/layout/zero-sized-array-enum-niche.stderr b/tests/ui/layout/zero-sized-array-enum-niche.stderr index d61408098df7..b6fcc14c063c 100644 --- a/tests/ui/layout/zero-sized-array-enum-niche.stderr +++ b/tests/ui/layout/zero-sized-array-enum-niche.stderr @@ -59,6 +59,7 @@ error: layout_of(Result<[u32; 0], bool>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -92,13 +93,15 @@ error: layout_of(Result<[u32; 0], bool>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/zero-sized-array-enum-niche.rs:13:1 + --> $DIR/zero-sized-array-enum-niche.rs:14:1 | LL | type AlignedResult = Result<[u32; 0], bool>; | ^^^^^^^^^^^^^^^^^^ @@ -164,6 +167,7 @@ error: layout_of(MultipleAlignments) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, }, Layout { size: Size(4 bytes), @@ -188,6 +192,7 @@ error: layout_of(MultipleAlignments) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -221,13 +226,15 @@ error: layout_of(MultipleAlignments) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/zero-sized-array-enum-niche.rs:21:1 + --> $DIR/zero-sized-array-enum-niche.rs:22:1 | LL | enum MultipleAlignments { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -293,6 +300,7 @@ error: layout_of(Result<[u32; 0], Packed>>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(3 bytes), @@ -326,13 +334,15 @@ error: layout_of(Result<[u32; 0], Packed>>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/zero-sized-array-enum-niche.rs:37:1 + --> $DIR/zero-sized-array-enum-niche.rs:38:1 | LL | type NicheLosesToTagged = Result<[u32; 0], Packed>>; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -402,6 +412,7 @@ error: layout_of(Result<[u32; 0], Packed>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -435,13 +446,15 @@ error: layout_of(Result<[u32; 0], Packed>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/zero-sized-array-enum-niche.rs:44:1 + --> $DIR/zero-sized-array-enum-niche.rs:45:1 | LL | type NicheWinsOverTagged = Result<[u32; 0], Packed>; | ^^^^^^^^^^^^^^^^^^^^^^^^ 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-2.rs b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.rs index 09ee9accccd2..66e6eb91a22e 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.rs +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.rs @@ -1,6 +1,6 @@ fn foo(&mut (ref mut v, w): &mut (&u8, &u8), x: &u8) { - *v = x; //~^ ERROR lifetime may not live long enough + *v = x; } fn main() { } diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.stderr index 30083b5ef546..e7cab52084dd 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.stderr @@ -1,13 +1,15 @@ error: lifetime may not live long enough - --> $DIR/ex3-both-anon-regions-2.rs:2:5 + --> $DIR/ex3-both-anon-regions-2.rs:1:14 | LL | fn foo(&mut (ref mut v, w): &mut (&u8, &u8), x: &u8) { - | - - let's call the lifetime of this reference `'1` - | | - | let's call the lifetime of this reference `'2` -LL | *v = x; - | ^^^^^^ assignment requires that `'1` must outlive `'2` + | ^^^^^^^^^ - - let's call the lifetime of this reference `'1` + | | | + | | let's call the lifetime of this reference `'2` + | assignment requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `&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>(&mut (ref mut v, w): &mut (&'a u8, &u8), x: &'a u8) { 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/invalid_value.stderr b/tests/ui/lint/invalid_value.stderr index b4e7421829f8..cc6a2a1c8e53 100644 --- a/tests/ui/lint/invalid_value.stderr +++ b/tests/ui/lint/invalid_value.stderr @@ -323,7 +323,7 @@ LL | let _val: (NonZero, i32) = mem::zeroed(); | ^^^^^^^^^^^^^ this code causes undefined behavior when executed | = note: `std::num::NonZero` must be non-null - = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null + = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null error: the type `(NonZero, i32)` does not permit being left uninitialized --> $DIR/invalid_value.rs:95:41 @@ -332,7 +332,7 @@ LL | let _val: (NonZero, i32) = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed | = note: `std::num::NonZero` must be non-null - = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null + = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null = note: integers must be initialized error: the type `*const dyn Send` does not permit zero-initialization @@ -415,7 +415,7 @@ note: because `std::num::NonZero` must be non-null (in this field of the on | LL | Banana(NonZero), | ^^^^^^^^^^^^ - = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null + = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null error: the type `OneFruitNonZero` does not permit being left uninitialized --> $DIR/invalid_value.rs:107:37 @@ -429,7 +429,7 @@ note: because `std::num::NonZero` must be non-null (in this field of the on | LL | Banana(NonZero), | ^^^^^^^^^^^^ - = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null + = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null = note: integers must be initialized error: the type `bool` does not permit being left uninitialized @@ -602,7 +602,7 @@ LL | let _val: NonZero = mem::transmute(0); | ^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed | = note: `std::num::NonZero` must be non-null - = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null + = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null error: the type `NonNull` does not permit zero-initialization --> $DIR/invalid_value.rs:156:34 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/macros/stringify.rs b/tests/ui/macros/stringify.rs index a560bf4c6ef4..40033f546d30 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -569,7 +569,7 @@ fn test_pat() { c1!(pat, [ &pat ], "&pat"); c1!(pat, [ &mut pat ], "&mut pat"); - // PatKind::Lit + // PatKind::Expr c1!(pat, [ 1_000_i8 ], "1_000_i8"); // PatKind::Range diff --git a/tests/ui/methods/bad-wf-when-selecting-method.rs b/tests/ui/methods/bad-wf-when-selecting-method.rs new file mode 100644 index 000000000000..638d1ffa982f --- /dev/null +++ b/tests/ui/methods/bad-wf-when-selecting-method.rs @@ -0,0 +1,18 @@ +trait Wf { + type Assoc; +} + +struct Wrapper, U>(T); + +trait Trait { + fn needs_sized(self); +} + +fn test(t: T) { + Wrapper(t).needs_sized(); + //~^ ERROR the trait bound `T: Wf` is not satisfied + //~| ERROR the trait bound `T: Wf` is not satisfied + //~| the method `needs_sized` exists for struct `Wrapper`, but its trait bounds were not satisfied +} + +fn main() {} diff --git a/tests/ui/methods/bad-wf-when-selecting-method.stderr b/tests/ui/methods/bad-wf-when-selecting-method.stderr new file mode 100644 index 000000000000..e6d500349670 --- /dev/null +++ b/tests/ui/methods/bad-wf-when-selecting-method.stderr @@ -0,0 +1,54 @@ +error[E0277]: the trait bound `T: Wf` is not satisfied + --> $DIR/bad-wf-when-selecting-method.rs:12:13 + | +LL | Wrapper(t).needs_sized(); + | ------- ^ the trait `Wf` is not implemented for `T` + | | + | required by a bound introduced by this call + | +note: required by a bound in `Wrapper` + --> $DIR/bad-wf-when-selecting-method.rs:5:19 + | +LL | struct Wrapper, U>(T); + | ^^^^^^^^^^^^^ required by this bound in `Wrapper` +help: consider restricting type parameter `T` with trait `Wf` + | +LL | fn test(t: T) { + | ++++ + +error[E0277]: the trait bound `T: Wf` is not satisfied + --> $DIR/bad-wf-when-selecting-method.rs:12:5 + | +LL | Wrapper(t).needs_sized(); + | ^^^^^^^^^^ the trait `Wf` is not implemented for `T` + | +note: required by a bound in `Wrapper` + --> $DIR/bad-wf-when-selecting-method.rs:5:19 + | +LL | struct Wrapper, U>(T); + | ^^^^^^^^^^^^^ required by this bound in `Wrapper` +help: consider restricting type parameter `T` with trait `Wf` + | +LL | fn test(t: T) { + | ++++ + +error[E0599]: the method `needs_sized` exists for struct `Wrapper`, but its trait bounds were not satisfied + --> $DIR/bad-wf-when-selecting-method.rs:12:16 + | +LL | struct Wrapper, U>(T); + | ----------------------------------- method `needs_sized` not found for this struct +... +LL | Wrapper(t).needs_sized(); + | ^^^^^^^^^^^ method cannot be called on `Wrapper` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `T: Wf` +help: consider restricting the type parameter to satisfy the trait bound + | +LL | fn test(t: T) where T: Wf { + | +++++++++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. 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/pattern/patkind-litrange-no-expr.rs b/tests/ui/pattern/patkind-litrange-no-expr.rs index 7ef541cb5852..14d7dc737d65 100644 --- a/tests/ui/pattern/patkind-litrange-no-expr.rs +++ b/tests/ui/pattern/patkind-litrange-no-expr.rs @@ -6,7 +6,7 @@ macro_rules! enum_number { fn foo(value: i32) -> Option<$name> { match value { - $( $value => Some($name::$variant), )* // PatKind::Lit + $( $value => Some($name::$variant), )* // PatKind::Expr $( $value ..= 42 => Some($name::$variant), )* // PatKind::Range _ => None } diff --git a/tests/ui/pattern/struct-pattern-on-non-struct-resolve-error.rs b/tests/ui/pattern/struct-pattern-on-non-struct-resolve-error.rs new file mode 100644 index 000000000000..17a5bad0e6c0 --- /dev/null +++ b/tests/ui/pattern/struct-pattern-on-non-struct-resolve-error.rs @@ -0,0 +1,10 @@ +// Regression test for #135209. +// We ensure that we don't try to access fields on a non-struct pattern type. +fn main() { + if let as Iterator>::Item { .. } = 1 { + //~^ ERROR E0658 + //~| ERROR E0071 + //~| ERROR E0277 + x //~ ERROR E0425 + } +} diff --git a/tests/ui/pattern/struct-pattern-on-non-struct-resolve-error.stderr b/tests/ui/pattern/struct-pattern-on-non-struct-resolve-error.stderr new file mode 100644 index 000000000000..793c2d1e97fc --- /dev/null +++ b/tests/ui/pattern/struct-pattern-on-non-struct-resolve-error.stderr @@ -0,0 +1,34 @@ +error[E0425]: cannot find value `x` in this scope + --> $DIR/struct-pattern-on-non-struct-resolve-error.rs:8:9 + | +LL | x + | ^ not found in this scope + +error[E0658]: usage of qualified paths in this context is experimental + --> $DIR/struct-pattern-on-non-struct-resolve-error.rs:4:12 + | +LL | if let as Iterator>::Item { .. } = 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #86935 for more information + = help: add `#![feature(more_qualified_paths)]` 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[E0071]: expected struct, variant or union type, found inferred type + --> $DIR/struct-pattern-on-non-struct-resolve-error.rs:4:12 + | +LL | if let as Iterator>::Item { .. } = 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a struct + +error[E0277]: `Vec<()>` is not an iterator + --> $DIR/struct-pattern-on-non-struct-resolve-error.rs:4:12 + | +LL | if let as Iterator>::Item { .. } = 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Vec<()>` is not an iterator + | + = help: the trait `Iterator` is not implemented for `Vec<()>` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0071, E0277, E0425, E0658. +For more information about an error, try `rustc --explain E0071`. diff --git a/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.rs b/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.rs index 225891e390ff..39f9f5a2c020 100644 --- a/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.rs +++ b/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.rs @@ -3,6 +3,10 @@ struct Website { title: Option, } +enum Foo { + Bar { a: i32 }, +} + fn main() { let website = Website { url: "http://www.example.com".into(), @@ -18,4 +22,9 @@ fn main() { println!("[{}]({})", title, url); //~ ERROR cannot find value `title` in this scope //~^ NOTE not found in this scope } + + let x = Foo::Bar { a: 1 }; + if let Foo::Bar { .. } = x { //~ NOTE this pattern + println!("{a}"); //~ ERROR cannot find value `a` in this scope + } } diff --git a/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.stderr b/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.stderr index 80fcd714400d..b985b771754e 100644 --- a/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.stderr +++ b/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.stderr @@ -1,5 +1,5 @@ error: expected `,` - --> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:12:31 + --> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:16:31 | LL | if let Website { url, Some(title) } = website { | ------- ^ @@ -7,13 +7,21 @@ LL | if let Website { url, Some(title) } = website { | while parsing the fields for this pattern error[E0425]: cannot find value `title` in this scope - --> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:18:30 + --> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:22:30 | LL | if let Website { url, .. } = website { | ------------------- this pattern doesn't include `title`, which is available in `Website` LL | println!("[{}]({})", title, url); | ^^^^^ not found in this scope -error: aborting due to 2 previous errors +error[E0425]: cannot find value `a` in this scope + --> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:28:20 + | +LL | if let Foo::Bar { .. } = x { + | --------------- this pattern doesn't include `a`, which is available in `Bar` +LL | println!("{a}"); + | ^ help: a local variable with a similar name exists: `x` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/print_type_sizes/niche-filling.stdout b/tests/ui/print_type_sizes/niche-filling.stdout index eeb5de532412..70612490a471 100644 --- a/tests/ui/print_type_sizes/niche-filling.stdout +++ b/tests/ui/print_type_sizes/niche-filling.stdout @@ -68,7 +68,7 @@ print-type-size type: `Union2, u32>`: 4 bytes, alignment: print-type-size variant `Union2`: 4 bytes print-type-size field `.a`: 4 bytes print-type-size field `.b`: 4 bytes, offset: 0 bytes, alignment: 4 bytes -print-type-size type: `core::num::nonzero::private::NonZeroU32Inner`: 4 bytes, alignment: 4 bytes +print-type-size type: `core::num::niche_types::NonZeroU32Inner`: 4 bytes, alignment: 4 bytes print-type-size field `.0`: 4 bytes print-type-size type: `std::num::NonZero`: 4 bytes, alignment: 4 bytes print-type-size field `.0`: 4 bytes 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/proc-macro/quote-debug.stdout b/tests/ui/proc-macro/quote-debug.stdout deleted file mode 100644 index d84b4e051e87..000000000000 --- a/tests/ui/proc-macro/quote-debug.stdout +++ /dev/null @@ -1,49 +0,0 @@ -#![feature(prelude_import)] -#![no_std] -//@ check-pass -//@ force-host -//@ no-prefer-dynamic -//@ compile-flags: -Z unpretty=expanded -//@ needs-unwind compiling proc macros with panic=abort causes a warning -// -// This file is not actually used as a proc-macro - instead, -// it's just used to show the output of the `quote!` macro - -#![feature(proc_macro_quote)] -#![crate_type = "proc-macro"] -#[prelude_import] -use ::std::prelude::rust_2015::*; -#[macro_use] -extern crate std; - -extern crate proc_macro; - -fn main() { - [crate::TokenStream::from(crate::TokenTree::Ident(crate::Ident::new("let", - crate::Span::recover_proc_macro_span(0)))), - crate::TokenStream::from(crate::TokenTree::Ident(crate::Ident::new("hello", - crate::Span::recover_proc_macro_span(1)))), - crate::TokenStream::from(crate::TokenTree::Punct(crate::Punct::new('=', - crate::Spacing::Alone))), - crate::TokenStream::from(crate::TokenTree::Literal({ - let mut iter = - "\"world\"".parse::().unwrap().into_iter(); - if let (Some(crate::TokenTree::Literal(mut lit)), None) = - (iter.next(), iter.next()) { - lit.set_span(crate::Span::recover_proc_macro_span(2)); - lit - } else { - ::core::panicking::panic("internal error: entered unreachable code") - } - })), - crate::TokenStream::from(crate::TokenTree::Punct(crate::Punct::new(';', - crate::Spacing::Alone)))].iter().cloned().collect::() -} -const _: () = - { - extern crate proc_macro; - #[rustc_proc_macro_decls] - #[used] - #[allow(deprecated)] - static _DECLS: &[proc_macro::bridge::client::ProcMacro] = &[]; - }; diff --git a/tests/ui/proc-macro/quote/auxiliary/basic.rs b/tests/ui/proc-macro/quote/auxiliary/basic.rs new file mode 100644 index 000000000000..ef726bbfbe3a --- /dev/null +++ b/tests/ui/proc-macro/quote/auxiliary/basic.rs @@ -0,0 +1,361 @@ +#![feature(proc_macro_quote)] +#![feature(proc_macro_totokens)] + +extern crate proc_macro; + +use std::borrow::Cow; +use std::ffi::{CStr, CString}; + +use proc_macro::*; + +#[proc_macro] +pub fn run_tests(_: TokenStream) -> TokenStream { + test_quote_impl(); + test_substitution(); + test_advanced(); + test_integer(); + test_floating(); + test_char(); + test_str(); + test_string(); + test_c_str(); + test_c_string(); + test_interpolated_literal(); + test_ident(); + test_underscore(); + test_duplicate(); + test_empty_quote(); + test_box_str(); + test_cow(); + test_append_tokens(); + test_outer_line_comment(); + test_inner_line_comment(); + test_outer_block_comment(); + test_inner_block_comment(); + test_outer_attr(); + test_inner_attr(); + test_quote_raw_id(); + + TokenStream::new() +} + +// Based on https://github.com/dtolnay/quote/blob/0245506323a3616daa2ee41c6ad0b871e4d78ae4/tests/test.rs +// +// FIXME(quote): +// The following tests are removed because they are not supported yet in `proc_macro::quote!` +// +// - quote_spanned: +// - fn test_quote_spanned_impl +// - fn test_type_inference_for_span +// - wrong-type-span.rs +// - format_ident: +// - fn test_format_ident +// - fn test_format_ident_strip_raw +// - repetition: +// - fn test_iter +// - fn test_array +// - fn test_fancy_repetition +// - fn test_nested_fancy_repetition +// - fn test_duplicate_name_repetition +// - fn test_duplicate_name_repetition_no_copy +// - fn test_btreeset_repetition +// - fn test_variable_name_conflict +// - fn test_nonrep_in_repetition +// - fn test_closure +// - fn test_star_after_repetition + +struct X; + +impl ToTokens for X { + fn to_tokens(&self, tokens: &mut TokenStream) { + Ident::new("X", Span::call_site()).to_tokens(tokens) + } +} + +fn test_quote_impl() { + let tokens = quote! { + impl<'a, T: ToTokens> ToTokens for &'a T { + fn to_tokens(&self, tokens: &mut TokenStream) { + (**self).to_tokens(tokens) + } + } + }; + + let expected = r#"impl < 'a, T : ToTokens > ToTokens for & 'a T +{ + fn to_tokens(& self, tokens : & mut TokenStream) + { (** self).to_tokens(tokens) } +}"#; + + assert_eq!(expected, tokens.to_string()); +} + +fn test_substitution() { + let x = X; + let tokens = quote!($x <$x> ($x) [$x] {$x}); + + let expected = "X (X) [X] { X }"; + + assert_eq!(expected, tokens.to_string()); +} + +fn test_advanced() { + let generics = quote!( <'a, T> ); + + let where_clause = quote!( where T: Serialize ); + + let field_ty = quote!(String); + + let item_ty = quote!(Cow<'a, str>); + + let path = quote!(SomeTrait::serialize_with); + + let value = quote!(self.x); + + let tokens = quote! { + struct SerializeWith $generics $where_clause { + value: &'a $field_ty, + phantom: ::std::marker::PhantomData<$item_ty>, + } + + impl $generics ::serde::Serialize for SerializeWith $generics $where_clause { + fn serialize(&self, s: &mut S) -> Result<(), S::Error> + where S: ::serde::Serializer + { + $path(self.value, s) + } + } + + SerializeWith { + value: $value, + phantom: ::std::marker::PhantomData::<$item_ty>, + } + }; + + let expected = r#"struct SerializeWith < 'a, T > where T : Serialize +{ + value : & 'a String, phantom : :: std :: marker :: PhantomData >, +} impl < 'a, T > :: serde :: Serialize for SerializeWith < 'a, T > where T : +Serialize +{ + fn serialize < S > (& self, s : & mut S) -> Result < (), S :: Error > + where S : :: serde :: Serializer + { SomeTrait :: serialize_with(self.value, s) } +} SerializeWith +{ + value : self.x, phantom : :: std :: marker :: PhantomData :: >, +}"#; + + assert_eq!(expected, tokens.to_string()); +} + +fn test_integer() { + let ii8 = -1i8; + let ii16 = -1i16; + let ii32 = -1i32; + let ii64 = -1i64; + let ii128 = -1i128; + let iisize = -1isize; + let uu8 = 1u8; + let uu16 = 1u16; + let uu32 = 1u32; + let uu64 = 1u64; + let uu128 = 1u128; + let uusize = 1usize; + + let tokens = quote! { + 1 1i32 1u256 + $ii8 $ii16 $ii32 $ii64 $ii128 $iisize + $uu8 $uu16 $uu32 $uu64 $uu128 $uusize + }; + let expected = r#"1 1i32 1u256 -1i8 -1i16 -1i32 -1i64 -1i128 -1isize 1u8 1u16 1u32 1u64 1u128 +1usize"#; + assert_eq!(expected, tokens.to_string()); +} + +fn test_floating() { + let e32 = 2.345f32; + + let e64 = 2.345f64; + + let tokens = quote! { + $e32 + $e64 + }; + let expected = concat!("2.345f32 2.345f64"); + assert_eq!(expected, tokens.to_string()); +} + +fn test_char() { + let zero = '\u{1}'; + let dollar = '$'; + let pound = '#'; + let quote = '"'; + let apost = '\''; + let newline = '\n'; + let heart = '\u{2764}'; + + let tokens = quote! { + $zero $dollar $pound $quote $apost $newline $heart + }; + let expected = "'\\u{1}' '$' '#' '\"' '\\'' '\\n' '\u{2764}'"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_str() { + let s = "\u{1} a 'b \" c"; + let tokens = quote!($s); + let expected = "\"\\u{1} a 'b \\\" c\""; + assert_eq!(expected, tokens.to_string()); +} + +fn test_string() { + let s = "\u{1} a 'b \" c".to_string(); + let tokens = quote!($s); + let expected = "\"\\u{1} a 'b \\\" c\""; + assert_eq!(expected, tokens.to_string()); +} + +fn test_c_str() { + let s = CStr::from_bytes_with_nul(b"\x01 a 'b \" c\0").unwrap(); + let tokens = quote!($s); + let expected = "c\"\\u{1} a 'b \\\" c\""; + assert_eq!(expected, tokens.to_string()); +} + +fn test_c_string() { + let s = CString::new(&b"\x01 a 'b \" c"[..]).unwrap(); + let tokens = quote!($s); + let expected = "c\"\\u{1} a 'b \\\" c\""; + assert_eq!(expected, tokens.to_string()); +} + +fn test_interpolated_literal() { + macro_rules! m { + ($literal:literal) => { + quote!($literal) + }; + } + + let tokens = m!(1); + let expected = "1"; + assert_eq!(expected, tokens.to_string()); + + let tokens = m!(-1); + let expected = "- 1"; + assert_eq!(expected, tokens.to_string()); + + let tokens = m!(true); + let expected = "true"; + assert_eq!(expected, tokens.to_string()); + + let tokens = m!(-true); + let expected = "- true"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_ident() { + let foo = Ident::new("Foo", Span::call_site()); + let bar = Ident::new(&format!("Bar{}", 7), Span::call_site()); + let tokens = quote!(struct $foo; enum $bar {}); + let expected = "struct Foo; enum Bar7 {}"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_underscore() { + let tokens = quote!(let _;); + let expected = "let _;"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_duplicate() { + let ch = 'x'; + + let tokens = quote!($ch $ch); + + let expected = "'x' 'x'"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_empty_quote() { + let tokens = quote!(); + assert_eq!("", tokens.to_string()); +} + +fn test_box_str() { + let b = "str".to_owned().into_boxed_str(); + let tokens = quote! { $b }; + assert_eq!("\"str\"", tokens.to_string()); +} + +fn test_cow() { + let owned: Cow = Cow::Owned(Ident::new("owned", Span::call_site())); + + let ident = Ident::new("borrowed", Span::call_site()); + let borrowed = Cow::Borrowed(&ident); + + let tokens = quote! { $owned $borrowed }; + assert_eq!("owned borrowed", tokens.to_string()); +} + +fn test_append_tokens() { + let mut a = quote!(a); + let b = quote!(b); + a.extend(b); + assert_eq!("a b", a.to_string()); +} + +fn test_outer_line_comment() { + let tokens = quote! { + /// doc + }; + let expected = "#[doc = \" doc\"]"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_inner_line_comment() { + let tokens = quote! { + //! doc + }; + let expected = "# ! [doc = \" doc\"]"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_outer_block_comment() { + let tokens = quote! { + /** doc */ + }; + let expected = "#[doc = \" doc \"]"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_inner_block_comment() { + let tokens = quote! { + /*! doc */ + }; + let expected = "# ! [doc = \" doc \"]"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_outer_attr() { + let tokens = quote! { + #[inline] + }; + let expected = "#[inline]"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_inner_attr() { + let tokens = quote! { + #![no_std] + }; + let expected = "#! [no_std]"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_quote_raw_id() { + let id = quote!(r#raw_id); + assert_eq!(id.to_string(), "r#raw_id"); +} diff --git a/tests/ui/proc-macro/quote/basic.rs b/tests/ui/proc-macro/quote/basic.rs new file mode 100644 index 000000000000..0336dbb78562 --- /dev/null +++ b/tests/ui/proc-macro/quote/basic.rs @@ -0,0 +1,8 @@ +//@ run-pass +//@ proc-macro: basic.rs + +extern crate basic; + +fn main() { + basic::run_tests!(); +} diff --git a/tests/ui/proc-macro/quote-debug.rs b/tests/ui/proc-macro/quote/debug.rs similarity index 91% rename from tests/ui/proc-macro/quote-debug.rs rename to tests/ui/proc-macro/quote/debug.rs index 11d144d609f5..ce113079e567 100644 --- a/tests/ui/proc-macro/quote-debug.rs +++ b/tests/ui/proc-macro/quote/debug.rs @@ -15,5 +15,6 @@ extern crate proc_macro; fn main() { proc_macro::quote! { let hello = "world"; + let r#raw_ident = r#"raw"literal"#; } } diff --git a/tests/ui/proc-macro/quote/debug.stdout b/tests/ui/proc-macro/quote/debug.stdout new file mode 100644 index 000000000000..3eaad9eb9699 --- /dev/null +++ b/tests/ui/proc-macro/quote/debug.stdout @@ -0,0 +1,72 @@ +#![feature(prelude_import)] +#![no_std] +//@ check-pass +//@ force-host +//@ no-prefer-dynamic +//@ compile-flags: -Z unpretty=expanded +//@ needs-unwind compiling proc macros with panic=abort causes a warning +// +// This file is not actually used as a proc-macro - instead, +// it's just used to show the output of the `quote!` macro + +#![feature(proc_macro_quote)] +#![crate_type = "proc-macro"] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; + +extern crate proc_macro; + +fn main() { + { + let mut ts = crate::TokenStream::new(); + crate::ToTokens::to_tokens(&crate::TokenTree::Ident(crate::Ident::new("let", + crate::Span::recover_proc_macro_span(0))), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Ident(crate::Ident::new("hello", + crate::Span::recover_proc_macro_span(1))), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new('=', + crate::Spacing::Alone)), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Literal({ + let mut iter = + "\"world\"".parse::().unwrap().into_iter(); + if let (Some(crate::TokenTree::Literal(mut lit)), None) = + (iter.next(), iter.next()) { + lit.set_span(crate::Span::recover_proc_macro_span(2)); + lit + } else { + ::core::panicking::panic("internal error: entered unreachable code") + } + }), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new(';', + crate::Spacing::Alone)), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Ident(crate::Ident::new("let", + crate::Span::recover_proc_macro_span(3))), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Ident(crate::Ident::new_raw("raw_ident", + crate::Span::recover_proc_macro_span(4))), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new('=', + crate::Spacing::Alone)), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Literal({ + let mut iter = + "r#\"raw\"literal\"#".parse::().unwrap().into_iter(); + if let (Some(crate::TokenTree::Literal(mut lit)), None) = + (iter.next(), iter.next()) { + lit.set_span(crate::Span::recover_proc_macro_span(5)); + lit + } else { + ::core::panicking::panic("internal error: entered unreachable code") + } + }), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new(';', + crate::Spacing::Alone)), &mut ts); + ts + } +} +const _: () = + { + extern crate proc_macro; + #[rustc_proc_macro_decls] + #[used] + #[allow(deprecated)] + static _DECLS: &[proc_macro::bridge::client::ProcMacro] = &[]; + }; diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs new file mode 100644 index 000000000000..2f67ae1bc6ed --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs @@ -0,0 +1,17 @@ +// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is +// expected to be incorrect. +//@ known-bug: #54722 + +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::quote; + +fn main() { + let nonrep = ""; + + // Without some protection against repetitions with no iterator somewhere + // inside, this would loop infinitely. + quote!($($nonrep $nonrep)*); +} diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr new file mode 100644 index 000000000000..5f28a46f3181 --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr @@ -0,0 +1,10 @@ +error: proc macro panicked + --> $DIR/does-not-have-iter-interpolated-dup.rs:16:5 + | +LL | quote!($($nonrep $nonrep)*); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: `$` must be followed by an ident or `$` in `quote!` + +error: aborting due to 1 previous error + diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs new file mode 100644 index 000000000000..1efb3eac6424 --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs @@ -0,0 +1,17 @@ +// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is +// expected to be incorrect. +//@ known-bug: #54722 + +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::quote; + +fn main() { + let nonrep = ""; + + // Without some protection against repetitions with no iterator somewhere + // inside, this would loop infinitely. + quote!($($nonrep)*); +} diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr new file mode 100644 index 000000000000..595aa8587634 --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr @@ -0,0 +1,10 @@ +error: proc macro panicked + --> $DIR/does-not-have-iter-interpolated.rs:16:5 + | +LL | quote!($($nonrep)*); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: message: `$` must be followed by an ident or `$` in `quote!` + +error: aborting due to 1 previous error + diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs b/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs new file mode 100644 index 000000000000..5f2ddabc390d --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs @@ -0,0 +1,13 @@ +// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is +// expected to be incorrect. +//@ known-bug: #54722 + +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::quote; + +fn main() { + quote!($(a b),*); +} diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr new file mode 100644 index 000000000000..f6f5d7e007d0 --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr @@ -0,0 +1,10 @@ +error: proc macro panicked + --> $DIR/does-not-have-iter-separated.rs:12:5 + | +LL | quote!($(a b),*); + | ^^^^^^^^^^^^^^^^ + | + = help: message: `$` must be followed by an ident or `$` in `quote!` + +error: aborting due to 1 previous error + diff --git a/tests/ui/proc-macro/quote/does-not-have-iter.rs b/tests/ui/proc-macro/quote/does-not-have-iter.rs new file mode 100644 index 000000000000..25ffd786cc61 --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter.rs @@ -0,0 +1,13 @@ +// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is +// expected to be incorrect. +//@ known-bug: #54722 + +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::quote; + +fn main() { + quote!($(a b)*); +} diff --git a/tests/ui/proc-macro/quote/does-not-have-iter.stderr b/tests/ui/proc-macro/quote/does-not-have-iter.stderr new file mode 100644 index 000000000000..0ed1daffc8cd --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter.stderr @@ -0,0 +1,10 @@ +error: proc macro panicked + --> $DIR/does-not-have-iter.rs:12:5 + | +LL | quote!($(a b)*); + | ^^^^^^^^^^^^^^^ + | + = help: message: `$` must be followed by an ident or `$` in `quote!` + +error: aborting due to 1 previous error + diff --git a/tests/ui/proc-macro/quote/not-quotable.rs b/tests/ui/proc-macro/quote/not-quotable.rs new file mode 100644 index 000000000000..7e38b4410528 --- /dev/null +++ b/tests/ui/proc-macro/quote/not-quotable.rs @@ -0,0 +1,12 @@ +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use std::net::Ipv4Addr; + +use proc_macro::quote; + +fn main() { + let ip = Ipv4Addr::LOCALHOST; + let _ = quote! { $ip }; //~ ERROR the trait bound `Ipv4Addr: ToTokens` is not satisfied +} diff --git a/tests/ui/proc-macro/quote/not-quotable.stderr b/tests/ui/proc-macro/quote/not-quotable.stderr new file mode 100644 index 000000000000..e349b2dce530 --- /dev/null +++ b/tests/ui/proc-macro/quote/not-quotable.stderr @@ -0,0 +1,24 @@ +error[E0277]: the trait bound `Ipv4Addr: ToTokens` is not satisfied + --> $DIR/not-quotable.rs:11:13 + | +LL | let _ = quote! { $ip }; + | ^^^^^^^^^^^^^^ + | | + | the trait `ToTokens` is not implemented for `Ipv4Addr` + | required by a bound introduced by this call + | + = help: the following other types implement trait `ToTokens`: + &T + &mut T + Box + CString + Cow<'_, T> + Option + Rc + bool + and 24 others + = note: this error originates in the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/proc-macro/quote/not-repeatable.rs b/tests/ui/proc-macro/quote/not-repeatable.rs new file mode 100644 index 000000000000..d115da731815 --- /dev/null +++ b/tests/ui/proc-macro/quote/not-repeatable.rs @@ -0,0 +1,16 @@ +// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is +// expected to be incorrect. +//@ known-bug: #54722 + +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::quote; + +struct Ipv4Addr; + +fn main() { + let ip = Ipv4Addr; + let _ = quote! { $($ip)* }; +} diff --git a/tests/ui/proc-macro/quote/not-repeatable.stderr b/tests/ui/proc-macro/quote/not-repeatable.stderr new file mode 100644 index 000000000000..18fbcd737985 --- /dev/null +++ b/tests/ui/proc-macro/quote/not-repeatable.stderr @@ -0,0 +1,10 @@ +error: proc macro panicked + --> $DIR/not-repeatable.rs:15:13 + | +LL | let _ = quote! { $($ip)* }; + | ^^^^^^^^^^^^^^^^^^ + | + = help: message: `$` must be followed by an ident or `$` in `quote!` + +error: aborting due to 1 previous error + 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/repeat-expr/repeat_count.stderr b/tests/ui/repeat-expr/repeat_count.stderr index 350ac287507a..c4aebfb0e20d 100644 --- a/tests/ui/repeat-expr/repeat_count.stderr +++ b/tests/ui/repeat-expr/repeat_count.stderr @@ -15,6 +15,12 @@ error[E0308]: mismatched types LL | let b = [0; ()]; | ^^ expected `usize`, found `()` +error[E0308]: mismatched types + --> $DIR/repeat_count.rs:31:17 + | +LL | let g = [0; G { g: () }]; + | ^^^^^^^^^^^ expected `usize`, found `G` + error[E0308]: mismatched types --> $DIR/repeat_count.rs:10:17 | @@ -33,12 +39,6 @@ error[E0308]: mismatched types LL | let e = [0; "foo"]; | ^^^^^ expected `usize`, found `&str` -error[E0308]: mismatched types - --> $DIR/repeat_count.rs:31:17 - | -LL | let g = [0; G { g: () }]; - | ^^^^^^^^^^^ expected `usize`, found `G` - error[E0308]: mismatched types --> $DIR/repeat_count.rs:19:17 | diff --git a/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs new file mode 100644 index 000000000000..c76e7a1d7166 --- /dev/null +++ b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs @@ -0,0 +1,70 @@ +#[derive(Copy, Clone)] +struct Type; + +struct NewType; + +const fn get_size() -> usize { + 10 +} + +fn get_dyn_size() -> usize { + 10 +} + +fn main() { + let a = ["a", 10]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create an array + + const size_b: usize = 20; + let b = [Type, size_b]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create an array + + let size_c: usize = 13; + let c = [Type, size_c]; + //~^ ERROR mismatched types + + const size_d: bool = true; + let d = [Type, size_d]; + //~^ ERROR mismatched types + + let e = [String::new(), 10]; + //~^ ERROR mismatched types + //~| HELP try using a conversion method + + let f = ["f", get_size()]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create an array + + let m = ["m", get_dyn_size()]; + //~^ ERROR mismatched types + + // is_vec, is_clone, is_usize_like + let g = vec![String::new(), 10]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create a vector + + let dyn_size = 10; + let h = vec![Type, dyn_size]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create a vector + + let i = vec![Type, get_dyn_size()]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create a vector + + let k = vec!['c', 10]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create a vector + + let j = vec![Type, 10_u8]; + //~^ ERROR mismatched types + + let l = vec![NewType, 10]; + //~^ ERROR mismatched types + + let byte_size: u8 = 10; + let h = vec![Type, byte_size]; + //~^ ERROR mismatched types +} diff --git a/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr new file mode 100644 index 000000000000..95eddbde9e67 --- /dev/null +++ b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr @@ -0,0 +1,124 @@ +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:15:19 + | +LL | let a = ["a", 10]; + | ^^ expected `&str`, found integer + | +help: replace the comma with a semicolon to create an array + | +LL | let a = ["a"; 10]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:20:20 + | +LL | let b = [Type, size_b]; + | ^^^^^^ expected `Type`, found `usize` + | +help: replace the comma with a semicolon to create an array + | +LL | let b = [Type; size_b]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:25:20 + | +LL | let c = [Type, size_c]; + | ^^^^^^ expected `Type`, found `usize` + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:29:20 + | +LL | let d = [Type, size_d]; + | ^^^^^^ expected `Type`, found `bool` + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:32:29 + | +LL | let e = [String::new(), 10]; + | ^^- help: try using a conversion method: `.to_string()` + | | + | expected `String`, found integer + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:36:19 + | +LL | let f = ["f", get_size()]; + | ^^^^^^^^^^ expected `&str`, found `usize` + | +help: replace the comma with a semicolon to create an array + | +LL | let f = ["f"; get_size()]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:40:19 + | +LL | let m = ["m", get_dyn_size()]; + | ^^^^^^^^^^^^^^ expected `&str`, found `usize` + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:44:33 + | +LL | let g = vec![String::new(), 10]; + | ^^ expected `String`, found integer + | +help: replace the comma with a semicolon to create a vector + | +LL | let g = vec![String::new(); 10]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:49:24 + | +LL | let h = vec![Type, dyn_size]; + | ^^^^^^^^ expected `Type`, found integer + | +help: replace the comma with a semicolon to create a vector + | +LL | let h = vec![Type; dyn_size]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:53:24 + | +LL | let i = vec![Type, get_dyn_size()]; + | ^^^^^^^^^^^^^^ expected `Type`, found `usize` + | +help: replace the comma with a semicolon to create a vector + | +LL | let i = vec![Type; get_dyn_size()]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:57:23 + | +LL | let k = vec!['c', 10]; + | ^^ expected `char`, found `u8` + | +help: replace the comma with a semicolon to create a vector + | +LL | let k = vec!['c'; 10]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:61:24 + | +LL | let j = vec![Type, 10_u8]; + | ^^^^^ expected `Type`, found `u8` + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:64:27 + | +LL | let l = vec![NewType, 10]; + | ^^ expected `NewType`, found integer + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:68:24 + | +LL | let h = vec![Type, byte_size]; + | ^^^^^^^^^ expected `Type`, found `u8` + +error: aborting due to 14 previous errors + +For more information about this error, try `rustc --explain E0308`. 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/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr b/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr index 64a0cb7f31a1..8e8f1d159b74 100644 --- a/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr +++ b/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr @@ -55,13 +55,15 @@ error: layout_of(Univariant) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:38:1 + --> $DIR/repr-c-dead-variants.rs:39:1 | LL | enum Univariant { | ^^^^^^^^^^^^^^^ @@ -137,6 +139,7 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(8 bytes), @@ -173,13 +176,15 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:45:1 + --> $DIR/repr-c-dead-variants.rs:46:1 | LL | enum TwoVariants { | ^^^^^^^^^^^^^^^^ @@ -247,6 +252,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, Layout { size: Size(16 bytes), @@ -271,6 +277,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, ], }, @@ -278,8 +285,9 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:57:1 + --> $DIR/repr-c-dead-variants.rs:58:1 | LL | enum DeadBranchHasOtherField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr b/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr index 5c4daa6d5197..2cd0960ce3ee 100644 --- a/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr +++ b/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr @@ -55,13 +55,15 @@ error: layout_of(Univariant) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:38:1 + --> $DIR/repr-c-dead-variants.rs:39:1 | LL | enum Univariant { | ^^^^^^^^^^^^^^^ @@ -137,6 +139,7 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -173,13 +176,15 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:45:1 + --> $DIR/repr-c-dead-variants.rs:46:1 | LL | enum TwoVariants { | ^^^^^^^^^^^^^^^^ @@ -247,6 +252,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, Layout { size: Size(16 bytes), @@ -271,6 +277,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, ], }, @@ -278,8 +285,9 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:57:1 + --> $DIR/repr-c-dead-variants.rs:58:1 | LL | enum DeadBranchHasOtherField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr b/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr index 64a0cb7f31a1..8e8f1d159b74 100644 --- a/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr +++ b/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr @@ -55,13 +55,15 @@ error: layout_of(Univariant) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:38:1 + --> $DIR/repr-c-dead-variants.rs:39:1 | LL | enum Univariant { | ^^^^^^^^^^^^^^^ @@ -137,6 +139,7 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(8 bytes), @@ -173,13 +176,15 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:45:1 + --> $DIR/repr-c-dead-variants.rs:46:1 | LL | enum TwoVariants { | ^^^^^^^^^^^^^^^^ @@ -247,6 +252,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, Layout { size: Size(16 bytes), @@ -271,6 +277,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, ], }, @@ -278,8 +285,9 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:57:1 + --> $DIR/repr-c-dead-variants.rs:58:1 | LL | enum DeadBranchHasOtherField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/repr/repr-c-dead-variants.rs b/tests/ui/repr/repr-c-dead-variants.rs index 3e8ae3d096d8..99f20982a996 100644 --- a/tests/ui/repr/repr-c-dead-variants.rs +++ b/tests/ui/repr/repr-c-dead-variants.rs @@ -7,6 +7,7 @@ // See also: repr-c-int-dead-variants.rs //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$SOME_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" // This test depends on the value of the `c_enum_min_bits` target option. // As there's no way to actually check it from UI test, we only run this test on a subset of archs. diff --git a/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr b/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr index 64a0cb7f31a1..8e8f1d159b74 100644 --- a/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr +++ b/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr @@ -55,13 +55,15 @@ error: layout_of(Univariant) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:38:1 + --> $DIR/repr-c-dead-variants.rs:39:1 | LL | enum Univariant { | ^^^^^^^^^^^^^^^ @@ -137,6 +139,7 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(8 bytes), @@ -173,13 +176,15 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:45:1 + --> $DIR/repr-c-dead-variants.rs:46:1 | LL | enum TwoVariants { | ^^^^^^^^^^^^^^^^ @@ -247,6 +252,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, Layout { size: Size(16 bytes), @@ -271,6 +277,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, ], }, @@ -278,8 +285,9 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:57:1 + --> $DIR/repr-c-dead-variants.rs:58:1 | LL | enum DeadBranchHasOtherField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/repr/repr-c-int-dead-variants.rs b/tests/ui/repr/repr-c-int-dead-variants.rs index 627569e080d4..723e57302440 100644 --- a/tests/ui/repr/repr-c-int-dead-variants.rs +++ b/tests/ui/repr/repr-c-int-dead-variants.rs @@ -4,6 +4,7 @@ // See also: repr-c-dead-variants.rs //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$SOME_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" // A simple uninhabited type. enum Void {} diff --git a/tests/ui/repr/repr-c-int-dead-variants.stderr b/tests/ui/repr/repr-c-int-dead-variants.stderr index 75005a64523a..fa08b323dec2 100644 --- a/tests/ui/repr/repr-c-int-dead-variants.stderr +++ b/tests/ui/repr/repr-c-int-dead-variants.stderr @@ -55,13 +55,15 @@ error: layout_of(UnivariantU8) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-int-dead-variants.rs:14:1 + --> $DIR/repr-c-int-dead-variants.rs:15:1 | LL | enum UnivariantU8 { | ^^^^^^^^^^^^^^^^^ @@ -137,6 +139,7 @@ error: layout_of(TwoVariantsU8) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -173,13 +176,15 @@ error: layout_of(TwoVariantsU8) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-int-dead-variants.rs:21:1 + --> $DIR/repr-c-int-dead-variants.rs:22:1 | LL | enum TwoVariantsU8 { | ^^^^^^^^^^^^^^^^^^ @@ -247,6 +252,7 @@ error: layout_of(DeadBranchHasOtherFieldU8) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, Layout { size: Size(16 bytes), @@ -271,6 +277,7 @@ error: layout_of(DeadBranchHasOtherFieldU8) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, ], }, @@ -278,8 +285,9 @@ error: layout_of(DeadBranchHasOtherFieldU8) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-int-dead-variants.rs:33:1 + --> $DIR/repr-c-int-dead-variants.rs:34:1 | LL | enum DeadBranchHasOtherFieldU8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr b/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr index cf4c219215e0..90b63249eca1 100644 --- a/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr +++ b/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr @@ -4,6 +4,7 @@ error[E0658]: cannot call conditionally-const method `::deref` in co LL | self.0 | ^^^^^^ | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: see issue #67792 for more information = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date 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/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr index c0cfb18955ce..404df206e189 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr @@ -1,42 +1,3 @@ -error: associated item referring to unboxed trait object for its own trait - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:4:13 - | -LL | trait A: Sized { - | - in this trait -LL | fn f(a: A) -> A; - | ^ ^ - | -help: you might have meant to use `Self` to refer to the implementing type - | -LL | fn f(a: Self) -> Self; - | ~~~~ ~~~~ - -error: associated item referring to unboxed trait object for its own trait - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:10:13 - | -LL | trait B { - | - in this trait -LL | fn f(b: B) -> B; - | ^ ^ - | -help: you might have meant to use `Self` to refer to the implementing type - | -LL | fn f(b: Self) -> Self; - | ~~~~ ~~~~ - -error: associated item referring to unboxed trait object for its own trait - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:16:20 - | -LL | trait C { - | - in this trait -LL | fn f(&self, c: C) -> C; - | ^ ^ - | -help: you might have meant to use `Self` to refer to the implementing type - | -LL | fn f(&self, c: Self) -> Self; - | ~~~~ ~~~~ - error[E0782]: expected a type, found a trait --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:4:13 | @@ -118,6 +79,45 @@ help: `C` is dyn-incompatible, use `impl C` to return an opaque type, as long as LL | fn f(&self, c: C) -> impl C; | ++++ +error: associated item referring to unboxed trait object for its own trait + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:4:13 + | +LL | trait A: Sized { + | - in this trait +LL | fn f(a: A) -> A; + | ^ ^ + | +help: you might have meant to use `Self` to refer to the implementing type + | +LL | fn f(a: Self) -> Self; + | ~~~~ ~~~~ + +error: associated item referring to unboxed trait object for its own trait + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:10:13 + | +LL | trait B { + | - in this trait +LL | fn f(b: B) -> B; + | ^ ^ + | +help: you might have meant to use `Self` to refer to the implementing type + | +LL | fn f(b: Self) -> Self; + | ~~~~ ~~~~ + +error: associated item referring to unboxed trait object for its own trait + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:16:20 + | +LL | trait C { + | - in this trait +LL | fn f(&self, c: C) -> C; + | ^ ^ + | +help: you might have meant to use `Self` to refer to the implementing type + | +LL | fn f(&self, c: Self) -> Self; + | ~~~~ ~~~~ + error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/suggestions/issue-105761-suggest-self-for-closure.stderr b/tests/ui/suggestions/issue-105761-suggest-self-for-closure.stderr index bc97d32ebb6e..8ddea4b222e9 100644 --- a/tests/ui/suggestions/issue-105761-suggest-self-for-closure.stderr +++ b/tests/ui/suggestions/issue-105761-suggest-self-for-closure.stderr @@ -11,7 +11,7 @@ LL | self.qux(); LL | x(1); | - immutable borrow later used here | -help: try explicitly pass `&Self` into the Closure as an argument +help: try explicitly passing `&Self` into the closure as an argument | LL ~ let x = |this: &Self, v: i32| { LL ~ this.bar(); @@ -35,7 +35,7 @@ LL | self.qux(); LL | y(); | - immutable borrow later used here | -help: try explicitly pass `&Self` into the Closure as an argument +help: try explicitly passing `&Self` into the closure as an argument | LL ~ let y = |this: &Self| { LL ~ this.bar(); 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/const-drop-bound.rs b/tests/ui/traits/const-traits/const-drop-bound.rs index 398fb3906405..4819da7c3a40 100644 --- a/tests/ui/traits/const-traits/const-drop-bound.rs +++ b/tests/ui/traits/const-traits/const-drop-bound.rs @@ -1,5 +1,4 @@ -//@ known-bug: #110395 -// FIXME check-pass +//@ check-pass #![feature(const_trait_impl)] #![feature(const_precise_live_drops, const_destruct)] diff --git a/tests/ui/traits/const-traits/const-drop-bound.stderr b/tests/ui/traits/const-traits/const-drop-bound.stderr deleted file mode 100644 index 78ba0279566d..000000000000 --- a/tests/ui/traits/const-traits/const-drop-bound.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0277]: the trait bound `Foo: ~const Destruct` is not satisfied - --> $DIR/const-drop-bound.rs:23:9 - | -LL | foo(res) - | --- ^^^ - | | - | required by a bound introduced by this call - | -note: required by a bound in `foo` - --> $DIR/const-drop-bound.rs:9:61 - | -LL | const fn foo(res: Result) -> Option where E: ~const Destruct { - | ^^^^^^ required by this bound in `foo` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr b/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr index 7b2cafb61244..2b5e66b1a086 100644 --- a/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr @@ -1,9 +1,16 @@ -error[E0277]: the trait bound `ConstDropImplWithBounds: const Destruct` is not satisfied +error[E0277]: the trait bound `NonTrivialDrop: const A` is not satisfied --> $DIR/const-drop-fail-2.rs:31:23 | LL | const _: () = check::>( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | +note: required for `ConstDropImplWithBounds` to implement `const Drop` + --> $DIR/const-drop-fail-2.rs:25:25 + | +LL | impl const Drop for ConstDropImplWithBounds { + | ------ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here note: required by a bound in `check` --> $DIR/const-drop-fail-2.rs:21:19 | diff --git a/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr b/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr index 7b2cafb61244..2b5e66b1a086 100644 --- a/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr @@ -1,9 +1,16 @@ -error[E0277]: the trait bound `ConstDropImplWithBounds: const Destruct` is not satisfied +error[E0277]: the trait bound `NonTrivialDrop: const A` is not satisfied --> $DIR/const-drop-fail-2.rs:31:23 | LL | const _: () = check::>( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | +note: required for `ConstDropImplWithBounds` to implement `const Drop` + --> $DIR/const-drop-fail-2.rs:25:25 + | +LL | impl const Drop for ConstDropImplWithBounds { + | ------ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here note: required by a bound in `check` --> $DIR/const-drop-fail-2.rs:21:19 | diff --git a/tests/ui/traits/const-traits/const-drop-fail.precise.stderr b/tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr similarity index 87% rename from tests/ui/traits/const-traits/const-drop-fail.precise.stderr rename to tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr index 8b3e777a0b09..682f48fe07af 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.precise.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:32:5 + --> $DIR/const-drop-fail.rs:33:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -8,13 +8,13 @@ LL | NonTrivialDrop, | ^^^^^^^^^^^^^^ | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:23:19 + --> $DIR/const-drop-fail.rs:24:19 | LL | const fn check(_: T) {} | ^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:34:5 + --> $DIR/const-drop-fail.rs:35:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -23,7 +23,7 @@ LL | ConstImplWithDropGlue(NonTrivialDrop), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:23:19 + --> $DIR/const-drop-fail.rs:24:19 | LL | const fn check(_: T) {} | ^^^^^^ required by this bound in `check` diff --git a/tests/ui/traits/const-traits/const-drop-fail.stock.stderr b/tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr similarity index 87% rename from tests/ui/traits/const-traits/const-drop-fail.stock.stderr rename to tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr index 8b3e777a0b09..682f48fe07af 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.stock.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:32:5 + --> $DIR/const-drop-fail.rs:33:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -8,13 +8,13 @@ LL | NonTrivialDrop, | ^^^^^^^^^^^^^^ | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:23:19 + --> $DIR/const-drop-fail.rs:24:19 | LL | const fn check(_: T) {} | ^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:34:5 + --> $DIR/const-drop-fail.rs:35:5 | LL | const _: () = check($exp); | ----- required by a bound introduced by this call @@ -23,7 +23,7 @@ LL | ConstImplWithDropGlue(NonTrivialDrop), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:23:19 + --> $DIR/const-drop-fail.rs:24:19 | LL | const fn check(_: T) {} | ^^^^^^ required by this bound in `check` diff --git a/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr b/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr new file mode 100644 index 000000000000..682f48fe07af --- /dev/null +++ b/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied + --> $DIR/const-drop-fail.rs:33:5 + | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... +LL | NonTrivialDrop, + | ^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:24:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied + --> $DIR/const-drop-fail.rs:35:5 + | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... +LL | ConstImplWithDropGlue(NonTrivialDrop), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:24:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` + +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/const-drop-fail.old_stock.stderr b/tests/ui/traits/const-traits/const-drop-fail.old_stock.stderr new file mode 100644 index 000000000000..682f48fe07af --- /dev/null +++ b/tests/ui/traits/const-traits/const-drop-fail.old_stock.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied + --> $DIR/const-drop-fail.rs:33:5 + | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... +LL | NonTrivialDrop, + | ^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:24:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied + --> $DIR/const-drop-fail.rs:35:5 + | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... +LL | ConstImplWithDropGlue(NonTrivialDrop), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:24:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` + +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/const-drop-fail.rs b/tests/ui/traits/const-traits/const-drop-fail.rs index 5e05b9db474a..a7f3d5654de9 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.rs +++ b/tests/ui/traits/const-traits/const-drop-fail.rs @@ -1,8 +1,9 @@ -//@ compile-flags: -Znext-solver -//@ revisions: stock precise +//@[new_precise] compile-flags: -Znext-solver +//@[new_stock] compile-flags: -Znext-solver +//@ revisions: new_stock old_stock new_precise old_precise #![feature(const_trait_impl, const_destruct)] -#![cfg_attr(precise, feature(const_precise_live_drops))] +#![cfg_attr(any(new_precise, old_precise), feature(const_precise_live_drops))] use std::marker::{Destruct, PhantomData}; diff --git a/tests/ui/traits/const-traits/cross-crate.stock.stderr b/tests/ui/traits/const-traits/cross-crate.stock.stderr index 09bf9c023c84..7cdde5a079f2 100644 --- a/tests/ui/traits/const-traits/cross-crate.stock.stderr +++ b/tests/ui/traits/const-traits/cross-crate.stock.stderr @@ -1,9 +1,10 @@ error[E0658]: cannot call conditionally-const method `::func` in constant functions - --> $DIR/cross-crate.rs:22:5 + --> $DIR/cross-crate.rs:22:11 | LL | Const.func(); - | ^^^^^^^^^^^^ + | ^^^^^^ | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: see issue #67792 for more information = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/traits/const-traits/cross-crate.stocknc.stderr b/tests/ui/traits/const-traits/cross-crate.stocknc.stderr index e52e5609b01d..2358731c901f 100644 --- a/tests/ui/traits/const-traits/cross-crate.stocknc.stderr +++ b/tests/ui/traits/const-traits/cross-crate.stocknc.stderr @@ -1,19 +1,21 @@ error[E0658]: cannot call conditionally-const method `::func` in constant functions - --> $DIR/cross-crate.rs:19:5 + --> $DIR/cross-crate.rs:19:14 | LL | NonConst.func(); - | ^^^^^^^^^^^^^^^ + | ^^^^^^ | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: see issue #67792 for more information = help: add `#![feature(const_trait_impl)]` 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]: cannot call conditionally-const method `::func` in constant functions - --> $DIR/cross-crate.rs:22:5 + --> $DIR/cross-crate.rs:22:11 | LL | Const.func(); - | ^^^^^^^^^^^^ + | ^^^^^^ | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: see issue #67792 for more information = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date 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/const-traits/staged-api-user-crate.stderr b/tests/ui/traits/const-traits/staged-api-user-crate.stderr index bf7466b8e166..400c76fcaf49 100644 --- a/tests/ui/traits/const-traits/staged-api-user-crate.stderr +++ b/tests/ui/traits/const-traits/staged-api-user-crate.stderr @@ -4,6 +4,7 @@ error[E0658]: cannot call conditionally-const associated function ` for more information = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/traits/const-traits/super-traits-fail-3.nyn.stderr b/tests/ui/traits/const-traits/super-traits-fail-3.nyn.stderr index 8abda1c8f8af..024db4b6d68d 100644 --- a/tests/ui/traits/const-traits/super-traits-fail-3.nyn.stderr +++ b/tests/ui/traits/const-traits/super-traits-fail-3.nyn.stderr @@ -39,11 +39,12 @@ LL | #[cfg_attr(any(yyy, yny, nyy, nyn), const_trait)] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: cannot call conditionally-const method `::a` in constant functions - --> $DIR/super-traits-fail-3.rs:36:5 + --> $DIR/super-traits-fail-3.rs:36:7 | LL | x.a(); - | ^^^^^ + | ^^^ | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: see issue #67792 for more information = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/traits/const-traits/super-traits-fail-3.nyy.stderr b/tests/ui/traits/const-traits/super-traits-fail-3.nyy.stderr index 8abda1c8f8af..024db4b6d68d 100644 --- a/tests/ui/traits/const-traits/super-traits-fail-3.nyy.stderr +++ b/tests/ui/traits/const-traits/super-traits-fail-3.nyy.stderr @@ -39,11 +39,12 @@ LL | #[cfg_attr(any(yyy, yny, nyy, nyn), const_trait)] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: cannot call conditionally-const method `::a` in constant functions - --> $DIR/super-traits-fail-3.rs:36:5 + --> $DIR/super-traits-fail-3.rs:36:7 | LL | x.a(); - | ^^^^^ + | ^^^ | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: see issue #67792 for more information = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.rs b/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.rs index cf73fd8d31fb..f776a6ce4c12 100644 --- a/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.rs +++ b/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.rs @@ -1,3 +1,5 @@ +// Sets some arbitrarily large width for more consistent output (see #135288). +//@ compile-flags: --diagnostic-width=120 struct Argument; struct Return; diff --git a/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.stderr b/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.stderr index 5b89158b0dba..ba0af763975d 100644 --- a/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.stderr +++ b/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `fn(Argument) -> Return {function}: Trait` is not satisfied - --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:12:11 + --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:14:11 | LL | takes(function); | ----- ^^^^^^^^ the trait `Trait` is not implemented for fn item `fn(Argument) -> Return {function}` @@ -7,7 +7,7 @@ LL | takes(function); | required by a bound introduced by this call | note: required by a bound in `takes` - --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:9:18 + --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:11:18 | LL | fn takes(_: impl Trait) {} | ^^^^^ required by this bound in `takes` @@ -16,18 +16,18 @@ help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return`, LL | takes(function as fn(Argument) -> Return); | +++++++++++++++++++++++++ -error[E0277]: the trait bound `{closure@$DIR/bare-fn-no-impl-fn-ptr-99875.rs:14:11: 14:34}: Trait` is not satisfied - --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:14:11 +error[E0277]: the trait bound `{closure@$DIR/bare-fn-no-impl-fn-ptr-99875.rs:16:11: 16:34}: Trait` is not satisfied + --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:16:11 | LL | takes(|_: Argument| -> Return { todo!() }); | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound | | | required by a bound introduced by this call | - = help: the trait `Trait` is not implemented for closure `{closure@$DIR/bare-fn-no-impl-fn-ptr-99875.rs:14:11: 14:34}` + = help: the trait `Trait` is not implemented for closure `{closure@$DIR/bare-fn-no-impl-fn-ptr-99875.rs:16:11: 16:34}` = help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return` note: required by a bound in `takes` - --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:9:18 + --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:11:18 | LL | fn takes(_: impl Trait) {} | ^^^^^ required by this bound in `takes` diff --git a/tests/ui/traits/issue-106072.rs b/tests/ui/traits/issue-106072.rs index 696bd765ebc5..d75c26642c60 100644 --- a/tests/ui/traits/issue-106072.rs +++ b/tests/ui/traits/issue-106072.rs @@ -1,4 +1,6 @@ -#[derive(Clone)] //~ expected a type, found a trait +#[derive(Clone)] +//~^ expected a type, found a trait +//~| expected a type, found a trait struct Foo; trait Foo {} //~ the name `Foo` is defined multiple times fn main() {} diff --git a/tests/ui/traits/issue-106072.stderr b/tests/ui/traits/issue-106072.stderr index 4a48e4e898d3..3e0d6d88086f 100644 --- a/tests/ui/traits/issue-106072.stderr +++ b/tests/ui/traits/issue-106072.stderr @@ -1,5 +1,5 @@ error[E0428]: the name `Foo` is defined multiple times - --> $DIR/issue-106072.rs:3:1 + --> $DIR/issue-106072.rs:5:1 | LL | struct Foo; | ----------- previous definition of the type `Foo` here @@ -16,7 +16,16 @@ LL | #[derive(Clone)] | = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 2 previous errors +error[E0782]: expected a type, found a trait + --> $DIR/issue-106072.rs:1:10 + | +LL | #[derive(Clone)] + | ^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors Some errors have detailed explanations: E0428, E0782. For more information about an error, try `rustc --explain E0428`. diff --git a/tests/ui/traits/missing-for-type-in-impl.e2021.stderr b/tests/ui/traits/missing-for-type-in-impl.e2021.stderr index a49c5d9d45b7..e79bb0524e93 100644 --- a/tests/ui/traits/missing-for-type-in-impl.e2021.stderr +++ b/tests/ui/traits/missing-for-type-in-impl.e2021.stderr @@ -1,15 +1,3 @@ -error[E0277]: the trait bound `i64: Foo` is not satisfied - --> $DIR/missing-for-type-in-impl.rs:19:19 - | -LL | let x: i64 = >::id(10); - | ^^^ the trait `Foo` is not implemented for `i64` - | -help: this trait has no implementations, consider adding one - --> $DIR/missing-for-type-in-impl.rs:3:1 - | -LL | trait Foo { - | ^^^^^^^^^^^^ - error[E0782]: expected a type, found a trait --> $DIR/missing-for-type-in-impl.rs:8:6 | @@ -25,6 +13,18 @@ help: you might have intended to implement this trait for a given type LL | impl Foo for /* Type */ { | ++++++++++++++ +error[E0277]: the trait bound `i64: Foo` is not satisfied + --> $DIR/missing-for-type-in-impl.rs:19:19 + | +LL | let x: i64 = >::id(10); + | ^^^ the trait `Foo` is not implemented for `i64` + | +help: this trait has no implementations, consider adding one + --> $DIR/missing-for-type-in-impl.rs:3:1 + | +LL | trait Foo { + | ^^^^^^^^^^^^ + error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0782. 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/type-alias-impl-trait/hkl_forbidden4.stderr b/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr index 293c8ea09f17..feb161c3b048 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr @@ -1,3 +1,15 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/hkl_forbidden4.rs:12:1 + | +LL | async fn operation(_: &mut ()) -> () { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `FutNothing<'_>`, got `{async fn body of operation()}` + | +note: previous use here + --> $DIR/hkl_forbidden4.rs:14:5 + | +LL | call(operation).await + | ^^^^^^^^^^^^^^^ + error: item does not constrain `FutNothing::{opaque#0}`, but has it in its signature --> $DIR/hkl_forbidden4.rs:18:10 | @@ -35,18 +47,6 @@ LL | LL | call(operation).await | ^^^^^^^^^^^^^^^ -error: concrete type differs from previous defining opaque type use - --> $DIR/hkl_forbidden4.rs:12:1 - | -LL | async fn operation(_: &mut ()) -> () { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `FutNothing<'_>`, got `{async fn body of operation()}` - | -note: previous use here - --> $DIR/hkl_forbidden4.rs:14:5 - | -LL | call(operation).await - | ^^^^^^^^^^^^^^^ - error[E0792]: expected generic lifetime parameter, found `'any` --> $DIR/hkl_forbidden4.rs:22:1 | diff --git a/tests/ui/type/pattern_types/range_patterns.rs b/tests/ui/type/pattern_types/range_patterns.rs index ff87444b49e3..446a33195c8b 100644 --- a/tests/ui/type/pattern_types/range_patterns.rs +++ b/tests/ui/type/pattern_types/range_patterns.rs @@ -3,6 +3,7 @@ #![allow(incomplete_features)] //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$SOME_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" use std::pat::pattern_type; diff --git a/tests/ui/type/pattern_types/range_patterns.stderr b/tests/ui/type/pattern_types/range_patterns.stderr index 0eed7c2ce1ce..7da8cfd4dbc2 100644 --- a/tests/ui/type/pattern_types/range_patterns.stderr +++ b/tests/ui/type/pattern_types/range_patterns.stderr @@ -36,8 +36,9 @@ error: layout_of(NonZero) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/range_patterns.rs:10:1 + --> $DIR/range_patterns.rs:11:1 | LL | type X = std::num::NonZeroU32; | ^^^^^^ @@ -73,8 +74,9 @@ error: layout_of((u32) is 1..=) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/range_patterns.rs:12:1 + --> $DIR/range_patterns.rs:13:1 | LL | type Y = pattern_type!(u32 is 1..); | ^^^^^^ @@ -137,6 +139,7 @@ error: layout_of(Option<(u32) is 1..=>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(4 bytes), @@ -176,13 +179,15 @@ error: layout_of(Option<(u32) is 1..=>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/range_patterns.rs:14:1 + --> $DIR/range_patterns.rs:15:1 | LL | type Z = Option; | ^^^^^^ @@ -245,6 +250,7 @@ error: layout_of(Option>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(4 bytes), @@ -284,13 +290,15 @@ error: layout_of(Option>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/range_patterns.rs:16:1 + --> $DIR/range_patterns.rs:17:1 | LL | type A = Option; | ^^^^^^ @@ -333,8 +341,9 @@ error: layout_of(NonZeroU32New) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/range_patterns.rs:18:1 + --> $DIR/range_patterns.rs:19:1 | LL | struct NonZeroU32New(pattern_type!(u32 is 1..)); | ^^^^^^^^^^^^^^^^^^^^ 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/unpretty/expanded-exhaustive.rs b/tests/ui/unpretty/expanded-exhaustive.rs index e052627e71cf..26c253d049b2 100644 --- a/tests/ui/unpretty/expanded-exhaustive.rs +++ b/tests/ui/unpretty/expanded-exhaustive.rs @@ -651,8 +651,8 @@ mod patterns { let &mut pat; } - /// PatKind::Lit - fn pat_lit() { + /// PatKind::Expr + fn pat_expr() { let 1_000_i8; let -""; } diff --git a/tests/ui/unpretty/expanded-exhaustive.stdout b/tests/ui/unpretty/expanded-exhaustive.stdout index 132d00cd8edd..bd7aa1117cce 100644 --- a/tests/ui/unpretty/expanded-exhaustive.stdout +++ b/tests/ui/unpretty/expanded-exhaustive.stdout @@ -567,8 +567,8 @@ mod patterns { fn pat_deref() { let deref!(pat); } /// PatKind::Ref fn pat_ref() { let &pat; let &mut pat; } - /// PatKind::Lit - fn pat_lit() { let 1_000_i8; let -""; } + /// PatKind::Expr + fn pat_expr() { let 1_000_i8; let -""; } /// PatKind::Range fn pat_range() { let ..1; let 0..; let 0..1; let 0..=1; let -2..=-1; } /// PatKind::Slice diff --git a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout index c424b1afa346..43aa93c83bd3 100644 --- a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout +++ b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout @@ -14,4 +14,4 @@ extern crate std; fn main() ({ } as ()) -fn foo((-(128 as i8) as i8)...(127 as i8): i8) ({ } as ()) +fn foo(-128...127: i8) ({ } as ()) 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/tests/ui/variance/variance-uniquerc.rs b/tests/ui/variance/variance-uniquerc.rs new file mode 100644 index 000000000000..0c395ab06eaa --- /dev/null +++ b/tests/ui/variance/variance-uniquerc.rs @@ -0,0 +1,27 @@ +// regression test of https://github.com/rust-lang/rust/pull/133572#issuecomment-2543007164 +// we should also test UniqueArc once implemented +// +// inline comments explain how this code *would* compile if UniqueRc was still covariant + +#![feature(unique_rc_arc)] + +use std::rc::UniqueRc; + +fn extend_lifetime<'a, 'b>(x: &'a str) -> &'b str { + let r = UniqueRc::new(""); // UniqueRc<&'static str> + let w = UniqueRc::downgrade(&r); // Weak<&'static str> + let mut r = r; // [IF COVARIANT]: ==>> UniqueRc<&'a str> + *r = x; // assign the &'a str + let _r = UniqueRc::into_rc(r); // Rc<&'a str>, but we only care to activate the weak + let r = w.upgrade().unwrap(); // Rc<&'static str> + *r // &'static str, coerces to &'b str + //~^ ERROR lifetime may not live long enough +} + +fn main() { + let s = String::from("Hello World!"); + let r = extend_lifetime(&s); + println!("{r}"); + drop(s); + println!("{r}"); +} diff --git a/tests/ui/variance/variance-uniquerc.stderr b/tests/ui/variance/variance-uniquerc.stderr new file mode 100644 index 000000000000..1557f7e40c58 --- /dev/null +++ b/tests/ui/variance/variance-uniquerc.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/variance-uniquerc.rs:17:5 + | +LL | fn extend_lifetime<'a, 'b>(x: &'a str) -> &'b str { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | *r // &'static str, coerces to &'b str + | ^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to 1 previous error + diff --git a/triagebot.toml b/triagebot.toml index 436c88541a5f..5ee52bbf435c 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" @@ -385,6 +391,11 @@ trigger_files = [ "x.ps1" ] +[autolabel."A-bootstrap-stamp"] +trigger_files = [ + "src/bootstrap/src/utils/build_stamp.rs", +] + [autolabel."T-infra"] trigger_files = [ "src/ci", @@ -414,13 +425,20 @@ trigger_files = [ [autolabel."A-testsuite"] trigger_files = [ + "src/bootstrap/src/core/build_steps/test.rs", "src/ci", - "src/tools/compiletest", "src/tools/cargotest", - "src/tools/tidy", + "src/tools/compiletest", + "src/tools/miropt-test-tools", "src/tools/remote-test-server", "src/tools/remote-test-client", - "src/tools/tier-check" + "src/tools/rustdoc-gui-test", + "src/tools/suggest-tests", +] + +[autolabel."A-tidy"] +trigger_files = [ + "src/tools/tidy", ] [autolabel."A-meta"] @@ -498,6 +516,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 +842,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 +1013,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 +1234,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"]